发布于 3年前

Angular Universal 页面返回正确的响应码,比如 404 Not Found

默认通过 Angular 创建的 Angular Universal 项目,在路由配置中使用通配符 ** 仅仅只是进行路由跳转。

{
  path: '**', redirectTo: '/not-found'
}

运行在 node express 环境下时,即使在页面内通过下方代码发送 http 状态码依然无法正确返回如 404 之类的响应码,只会以 200 返回。

如果没有正确返回响应码,在生产环境下使用,则会出现这个问题:没有正确返回状态码,搜索引擎爬虫认为这是一个正常的页面,将会被爬取并索引,导致用户在搜索引擎搜索结果中访问到错误页,在用户眼里这可能无关紧要,但是对于搜索引擎来说,这是十分重要的,不处理将会对网站排名造成巨大的影响。

在页面中设置状态码

新增页面组件 not-found,并修改 not-found.component.ts:

import { Component, OnInit, Optional } from '@angular/core';
import { PLATFORM_ID, Inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { RESPONSE } from "@nguniversal/express-engine/tokens";
import { Response } from 'express';

@Component({
  selector: 'app-not-found',
  templateUrl: './not-found.component.html'
})

export class NotFoundComponent implements OnInit {

  constructor(
    @Inject(PLATFORM_ID) private platformId: Object,
    @Optional() @Inject(RESPONSE) private response: Response
  ) {
  }

  ngOnInit() {
    if (!isPlatformBrowser(this.platformId)) {
      if (this.response) {
        this.response.status(404); // 通过 RESPONSE 设置 HTTP 状态码,这里设置为 404
        this.response.statusMessage = 'Not Found'; // 设置 HTTP 响应实体内容
      }
    }
  }
}

上方代码运行在服务端渲染模式下,请求页面会返回一个HTTP响应码为 200 的 html 页面,这是因为在 server.ts 中没有使用 ngExpressEngine 函数处理响应并返回HTML页面。

ngExpressEngine() 是对 Universal 的 renderModule() 函数的封装。它会把客户端请求转换成服务端渲染的 HTML 页面。 你还要在某个适用于你服务端技术栈的模板引擎中调用这个函数。

第一个参数是 AppServerModule 。 它是 Universal 服务端渲染器和你的应用之间的桥梁。

第二个参数 extraProviders 是可选的。它能让你指定一些在服务端运行时特有的服务提供商。 只有当你的应用需要一些运行在服务器中才需要的信息时,才需要这么做。 比如这个运行中的服务器的源地址,当像前面例子中那样无法使用 Request 令牌时,可用它来计算 HTTP URL 的绝对地址。

ngExpressEngine() 函数返回了一个会解析成渲染好的页面的承诺( Promise )。 接下来你的引擎要决定拿这个页面做点什么。 在这个引擎的 Promise 回调函数中,把渲染好的页面返回给了 Web 服务器,然后服务器通过 HTTP 响应把它转发给了客户端。

Angular 9 的解决方案

需要通过修改 src/server.ts 内 app.engine 代码: 首先在代码开始处顶部引入下方的内容:

import {REQUEST, RESPONSE} from '@nguniversal/express-engine/tokens';
app.engine('html', (path, options, callback) => ngExpressEngine({
    bootstrap: AppServerModule,
    providers: [
      provideModuleMap(LAZY_MODULE_MAP),
      {
        provide: REQUEST,
        useValue: options.req,
      },
      {
        provide: RESPONSE,
        useValue: options.req.res,
      },
    ]
  })(path, options, callback)
);

低于 Angular 9(比如 Angular 8 等) 的解决方案

同样需要通过修改 src/server.ts 内 app.engine 代码: 首先在代码开始处顶部引入下方的内容:

import {REQUEST, RESPONSE} from '@nguniversal/express-engine/tokens';
app.engine('html', (path, options, callback) => ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [
      provideModuleMap(LAZY_MODULE_MAP),
      {
        provide: REQUEST,
        useValue: options.req,
      },
      {
        provide: RESPONSE,
        useValue: options.req.res,
      },
    ]
  })(path, options, callback)
);

以上的代码都是基于 Angular 默认生成的代码修改的。

重新运行服务端渲染项目,访问页面 not-found 即可在开发者工具中查看到正确的响应码了。

©2020 edoou.com   京ICP备16001874号-3