借助ASP.Net core 2.0 SpaPrerenderingExtensions
中的JavaScriptServices
,似乎我们可以从Angular(SSR)应用返回HTTP状态代码,SpaPrerenderingExtensions
将使用该状态代码来返回客户端的HTTP状态代码。
如果您在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
您会注意到,renderResult.Html
包含从Angular Server App返回的html标记,而renderResult.StatusCode.Value
包含状态代码。
用于启用SSR的官方文档(可在https://docs.microsoft.com/en-us/aspnet/core/spa/angular?view=aspnetcore-2.1&tabs=visual-studio#server-side-rendering获得)建议在main.server.ts
文件中添加以下代码。
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应用返回状态代码,以便ASP.Net Core 2.0 SpaPrerenderingExtensions
正确地获取它。
任何帮助将不胜感激。例如,如果我有一个具有状态码值的服务,该如何从该服务中选择值并传递到main.server.ts
文件中的服务器。
注意:我正在使用基于ASP.Net Core 2.1 Angular CLI 6的模板。
答案 0 :(得分:0)
好吧,我尝试了以下方法,而且似乎可行,任何反馈和改进都将受到欢迎。我不确定这种方法是否线程安全。我的意思是,如果并发请求由服务器处理,则一个请求的状态代码是否有可能分配给其他请求,因为这种方法利用了constant
对象的子属性可变的事实。
我使用以下代码创建了文件globals.ts
:
export const response = { statusCode: 200 };
然后,我创建了一个服务StatusCodeService
import { Injectable } from '@angular/core';
import * as myGlobals from './globals';
@Injectable()
export class StatusCodeService {
setStatusCode(statusCode: number) {
myGlobals.response.statusCode = statusCode;
}
}
现在,每当需要设置状态代码时,我都会使用此服务来设置状态代码。
我还使用以下代码更新了main.server.ts
文件,以将值传递给服务器:
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
}
});
});
我还尝试了Return 404 status code in aspnet core SPA Angular application
中提到的方法我为Angular 6更新了main.server.ts文件。
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: '<app-root></app-root>', 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<RenderResult>((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();
});
});
});
});
});
但是当我运行该应用程序时,它几乎坏掉了,并出现以下问题:
答案 1 :(得分:0)
我将其发布为答案,因为我可以设置格式,使其比注释中的格式更好,但这都是未经测试的,并且基于我过时的解决方案,上面的代码段以及Angular6文档(我没有最新的VS模板,因此我目前无法进行测试。
同时提供给renderModule()和renderModuleFactory()的options
参数对象有一个extraProviders
数组,我相信这将使我们向{{ 1}}模块。
这是我的主意:
AppServerModule
:Return 404 status code in aspnet core SPA Angular application HttpStatusCodeService
模块(仅浏览器模块,而不是服务器模块)的providers
数组中。
AppBrowserModule
文件中的HttpStatusCodeService
并将该实例添加为main.server.ts
数组中的提供者。
这是我的未测试尝试修改您的extraProviders
文件片段以使其正常工作。
main.server.ts
在原始解决方案中,我使用Angular组件渲染了404 Not Found页面,所以我不会覆盖返回的HTML(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
}
});
});
)。
为了完整起见,以下是答案上方的html: html, statusCode: statusCode
:
HttpStatusCodeService
让我知道是否可行。如果没有,我可以尝试建立一个测试环境以进一步解决这个问题,但是没有希望。