ASP.Net Core 2.1 Angular SSR/Universal返回Http状态码404

问题描述:

使用ASP.Net core 2.0 JavaScriptServices中的SpaPrerenderingExtensions,似乎我们可以从Angular(SSR)应用返回HTTP状态代码,SpaPrerenderingExtensions将使用该HTTP状态代码将HTTP状态代码返回给客户端

With SpaPrerenderingExtensions available in ASP.Net core 2.0 JavaScriptServices, it seems that we can return the HTTP Status Code from the Angular (SSR) App which SpaPrerenderingExtensions will use to return the HTTP Status Code to the client.

如果您在SpaPrerenderingExtensions

private static async Task ServePrerenderResult(HttpContext context, RenderToStringResult renderResult)

完整的代码SpaPrerenderingExtensions可在此处找到: https://github.com/aspnet/JavaScriptServices/blob/8ded472fe93123079ab97c9332f24460340876d2/src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/SpaPrerenderingExtensions.cs

Complete code of SpaPrerenderingExtensions is available here: https://github.com/aspnet/JavaScriptServices/blob/8ded472fe93123079ab97c9332f24460340876d2/src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/SpaPrerenderingExtensions.cs

您会注意到,renderResult.Html包含从Angular Server App返回的html标记,而renderResult.StatusCode.Value包含状态代码.

You will notice that renderResult.Html contains the html markup returned from Angular Server App whereas renderResult.StatusCode.Value contains the status code.

用于启用SSR的官方文档(可在

The official documentation for enabling SSR (available at https://docs.microsoft.com/en-us/aspnet/core/spa/angular?view=aspnetcore-2.1&tabs=visual-studio#server-side-rendering) suggest adding below code to the main.server.ts file.

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.server.module';

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 => ({ html }));
});

任何人都可以指导我如何从Angular Server App中返回状态代码,以便ASP.Net Core 2.0 SpaPrerenderingExtensions正确获取它.

Can anyone please guide how do I return the status code from the Angular Server App so that its properly picked up by the ASP.Net Core 2.0 SpaPrerenderingExtensions.

任何帮助将不胜感激.例如,如果我有一个具有状态码值的服务,该如何从该服务中选择值并传递到main.server.ts文件中的服务器.

Any help would be highly appreciated. For example if I have a service which has value of status code, how do I pick value from that service and pass to server in main.server.ts file.

注意:我正在使用基于ASP.Net Core 2.1 Angular CLI 6的模板.

Note: I am using ASP.Net Core 2.1 Angular CLI 6 based template.

我将其发布为答案,因为我可以设置比注释中更好的格式,但这都是未经测试的,并且基于我过时的解决方案,上面的代码段和Angular6文档(我没有最新的VS模板,因此目前无法测试).

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).

同时提供给 renderModule() renderModuleFactory()具有一个extraProviders数组,我相信它将允许我们添加AppServerModule模块的其他提供程序.

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.

这是我的主意:

  • 在这里的答案中使用我的HttpStatusCodeService:
  • 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.
    • 这将允许在应用程序在服务器上运行时注入服务,同时还为我们提供了对注入服务的引用.

    这是我的未试用尝试,以修改您的main.server.ts文件片段以使其正常工作.

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

    在原始解决方案中,我使用Angular组件渲染了404 Not Found页面,因此我不会覆盖返回的HTML(html: 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).

    为了完整起见,这是上面链接的答案中的HttpStatusCodeService:

    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.