3

I am working on a project using the Angular SPA project template for dotnet core (the one available in VS2017 and dotnet core 2.0).

I have an Angular component to display a "Not Found" message if a user goes to an invalid URL.

I also have server-side pre-rendering enabled. When returning the prerendered "Not Found" page from the server, how would I make it return an HTTP status code of 404?

Every method I have found to do this uses Express as the backend webserver, I have not been able to find any resources for doing this on an aspnet core backend.

Thanks for any help!


Edit for clarity:

I am not looking to return a 404 for a specific MVC controller action or on an application error.

I am looking to return a 404 from a specific Angular2/4 component, rendered server-side by Microsoft.AspNetCore.SpaServices.

For comparison, here is an example of a solution using the Express web server on NodeJS and angular universal for server-side rendering.

3
  • i think this will help you stackoverflow.com/questions/667053/… Commented Oct 16, 2017 at 19:06
  • better stackoverflow.com/questions/37690114/… Commented Oct 16, 2017 at 20:09
  • Thank you for the suggestions, but neither of these addresses my issue. They are both talking about returning 404 responses from a standard ASP.Net/ASPNet Core application and not a server-side rendered Angular component. I edited my question for clarity. Commented Oct 17, 2017 at 11:02

1 Answer 1

2

I have found a solution.

First, we need to create an injectable service that components can use to set the status code:

import { Injectable } from '@angular/core';

@Injectable()
export class HttpStatusCodeService {

  private statusCode: number;

  constructor(){
    this.statusCode = 200;
  }

  public setStatusCode(statusCode: number) {
    this.statusCode = statusCode;
  }

  public getStatusCode(): number {
    return this.statusCode;
  }
}

We will need to add this service to the providers array in the main AppModule module:

...
providers: [
    ...
    HttpStatusCodeService,
    ...
]
...

And then we need to add two lines (plus the import statement) within our boot.server.ts file (note this is based on the stock file created by the VS2017 template):

import 'reflect-metadata';
import 'zone.js';
import 'rxjs/add/operator/first';
import { APP_BASE_HREF } from '@angular/common';
import { enableProdMode, ApplicationRef, NgZone, ValueProvider } from '@angular/core';
import { platformDynamicServer, PlatformState, INITIAL_CONFIG } from '@angular/platform-server';
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
import { AppModule } from './app/app.module.server';

//ADD THIS LINE
import { HttpStatusCodeService } from './path/to/services/http-status-code.service';

enableProdMode();

export default createServerRenderer(params => {
    const providers = [
        { provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
        { provide: APP_BASE_HREF, useValue: params.baseUrl },
        { provide: 'BASE_URL', useValue: params.origin + params.baseUrl },
    ];

    return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
        const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
        const state = moduleRef.injector.get(PlatformState);
        const zone = moduleRef.injector.get(NgZone);

        //ADD THIS LINE: this will get the instance of the HttpStatusCodeService created for this request.
        const statusCodeService = moduleRef.injector.get(HttpStatusCodeService); 

        return new Promise<RenderResult>((resolve, reject) => {
            zone.onError.subscribe((errorInfo: any) => reject(errorInfo));
            appRef.isStable.first(isStable => isStable).subscribe(() => {
                // Because 'onStable' fires before 'onError', we have to delay slightly before
                // completing the request in case there's an error to report
                setImmediate(() => {
                    resolve({
                        html: state.renderToString(),

                        //ADD THIS LINE: this will get the currently set status code and return it along with the prerendered html string
                        statusCode: statusCodeService.getStatusCode() 
                    });
                    moduleRef.destroy();
                });
            });
        });
    });
});

And then finally we need to set the status code in any component that shouldn't return HTTP 200:

import { Component } from '@angular/core';

import { HttpStatusCodeService } from './path/to/services/http-status-code.service';

@Component({
    selector: 'not-found',
    templateUrl: './not-found.html'
})
export class NotFoundComponent {

  constructor(private httpStatusCodeService: HttpStatusCodeService) {
    httpStatusCodeService.setStatusCode(404);
  }
}
Sign up to request clarification or add additional context in comments.

3 Comments

can you please also guide how to handle this in new Angular CLI based template for .net github.com/aspnet/JavaScriptServices/issues/… . I think angular part would be mostly same but I am unable to figure out the .net portion.
There is no .NET portion, the answer above is all Angular. It looks like the Angular CLI based template changes a lot, including how the Angular application is built and bootstrapped. Skimming through the docs referenced in the template's Startup.cs file (go.microsoft.com/fwlink/?linkid=864501), it looks like you need to add a main.server.ts file that is similar to the boot.server.ts in the RTM template, however it is different enough that I have no idea how to handle this within it, nor do I have the time to try and figure it out right now.
Entith can you please take a look at this stackoverflow.com/a/50937983/2755616 and share your feedback on this approach for new Angular CLI Based template?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.