ASP.Net Core 2.1 Angular SSR/Universal returning Http Status Code 404

后端 未结 2 904
囚心锁ツ
囚心锁ツ 2021-01-15 23:00

With SpaPrerenderingExtensions available in ASP.Net core 2.0 JavaScriptServices, it seems that we can return the HTTP Status Code from the Angular

2条回答
  •  一向
    一向 (楼主)
    2021-01-15 23:33

    I am posting this as an answer because I can format things better than in a comment, however this is all untested and based of looking at my outdated solution, your code snippets above, and the Angular6 docs (I don't have the latest VS templates so I can't test at the moment).

    The options parameter object that is given to both renderModule() and renderModuleFactory() has an extraProviders array that, I believe, will let us add additional providers to the AppServerModule module.

    Here is my idea:

    • Use my HttpStatusCodeService from my answer here: Return 404 status code in aspnet core SPA Angular application
    • Add it to the providers array in the AppBrowserModule module (only the browser module, not the server module).
      • This will allow the service to be injected normally while the app is running in the browser.
    • Instantiate HttpStatusCodeService in main.server.ts file and add that instance as a provider in the extraProviders array.
      • This will allow the service to be injected while the app is running on the server while also giving us a reference to the injected service.
    • Use the reference to the service to determine if the response needs to be a 404.

    Here is my untested attempt to modify your main.server.ts file snippet to work.

    import 'zone.js/dist/zone-node';
    import 'reflect-metadata';
    import { renderModule, renderModuleFactory } from '@angular/platform-server';
    import { APP_BASE_HREF } from '@angular/common';
    import { enableProdMode } from '@angular/core';
    import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
    import { createServerRenderer } from 'aspnet-prerendering';
    export { AppServerModule } from './app/app.module.server';
    
    //ADD THIS LINE
    import { HttpStatusCodeService } from './path/to/services/http-status-code.service';
    
    enableProdMode();
    
    export default createServerRenderer(params => {
      const { AppServerModule, AppServerModuleNgFactory, LAZY_MODULE_MAP } = (module as any).exports;
    
      //ADD THIS LINE - We manually instantiate the service so we have a reference to it.
      let statusCodeService = new HttpStatusCodeService();
    
      const options = {
        document: params.data.originalHtml,
        url: params.url,
        extraProviders: [
          provideModuleMap(LAZY_MODULE_MAP),
          { provide: APP_BASE_HREF, useValue: params.baseUrl },
          { provide: 'BASE_URL', useValue: params.origin + params.baseUrl },
    
          //ADD THIS LINE - We add a provider for the service that uses our instance. 
          { provide: HttpStatusCodeService, useValue: statusCodeService },
        ]
      };
    
      const renderPromise = AppServerModuleNgFactory
        ? /* AoT */ renderModuleFactory(AppServerModuleNgFactory, options)
        : /* dev */ renderModule(AppServerModule, options);
    
      return renderPromise.then((html) => {
    
        //ADD THIS LINE - get the status code from the service.
        let statusCode: number = statusCodeService.getStatusCode();
    
        return {
          html: statusCode == 404 ? 'Page not found!' : html, StatusCode: statusCode
        }
    
      });
    });
    

    In my original solution, I rendered my 404 Not Found page using an Angular component, so I wouldn't override the returned HTML (html: html, statusCode: statusCode).

    And for the sake of completeness, here is the HttpStatusCodeService from the answer linked to above:

    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;
      }
    }
    

    Let me know if this works. If it doesn't, I can try to get a test environment set up to poke at this some more, but no promises.

提交回复
热议问题