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

后端 未结 2 896
囚心锁ツ
囚心锁ツ 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:39

    Ok, I tried below approach and it seems to work, any feedback and improvements would be highly welcomed. I am not sure if this approach is thread safe. What I mean is if concurrent requests are served by the server, is it possible that the status code of one request is assigned to other request since this approach is taking benefit of the fact that the sub properties of constant objects are mutable.

    I created a file globals.ts with below code:

    export const response = { statusCode: 200 };
    

    Then, I created a service StatusCodeService

    import { Injectable } from '@angular/core';
    import * as myGlobals from './globals';
    
    @Injectable()
    export class StatusCodeService {
    
      setStatusCode(statusCode: number) {
        myGlobals.response.statusCode = statusCode;
      }
    
    }
    

    Now whenever I have to set the status code, I use this service to set status code.

    I have also updated the main.server.ts file with below code to pass value to the server:

    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';
    
    import * as myGlobals from './app/globals';
    
    enableProdMode();
    
    export default createServerRenderer(params => {
      const { AppServerModule, AppServerModuleNgFactory, LAZY_MODULE_MAP } = (module as any).exports;
      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 }
        ]
      };
    
      const renderPromise = AppServerModuleNgFactory
        ? /* AoT */ renderModuleFactory(AppServerModuleNgFactory, options)
        : /* dev */ renderModule(AppServerModule, options);
    
      return renderPromise.then((html) => {
    
        let statusCode: number = 200;
        if (myGlobals.response.statusCode == 404) {
          statusCode = 404;
          //reset status code to 200 for next request
          myGlobals.response.statusCode = 200;
        }
    
        return {
          html: statusCode == 404 ? 'Page not found!' : html, StatusCode: statusCode
        }
    
      });
    });
    

    I also tried the approach mentioned in Return 404 status code in aspnet core SPA Angular application

    I updated the main.server.ts file for Angular 6.

    import 'zone.js/dist/zone-node';
    import 'reflect-metadata';
    import { APP_BASE_HREF } from '@angular/common';
    
    import { first } from 'rxjs/operators';
    
    import { enableProdMode, ApplicationRef, NgZone } from '@angular/core';
    import { platformDynamicServer, PlatformState, INITIAL_CONFIG } from '@angular/platform-server';
    import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
    import { AppServerModule } from './app/app.module.server';
    
    import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
    
    //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;
    
      const providers = [
        provideModuleMap(LAZY_MODULE_MAP),
        { provide: INITIAL_CONFIG, useValue: { document: '', url: params.url } },
        { provide: APP_BASE_HREF, useValue: params.baseUrl },
        { provide: 'BASE_URL', useValue: params.origin + params.baseUrl },
      ];
    
      return platformDynamicServer(providers).bootstrapModule(AppServerModule).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((resolve, reject) => {
          zone.onError.subscribe((errorInfo: any) => reject(errorInfo));
          appRef.isStable.pipe(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();
            });
          });
        });
      });
    });
    

    But when I run the app, its almost broken and have below issues:

    • There isn't any style sheet in the generated markup. Server Transfer State is missing. fxFlex layout doesn't work.

提交回复
热议问题