Angular 17部署Cloudflare Workers/Pages时如何传递HTTP状态码?
在Cloudflare Pages/Worker中为Angular SSR应用传递HTTP状态码与自定义请求头
我正尝试在Cloudflare Pages/Worker上部署Angular应用,已通过Cloudflare CLI生成了一个Angular 17测试应用,其server.ts代码如下:
import { renderApplication } from "@angular/platform-server"; import bootstrap from "./src/main.server"; interface Env { ASSETS: { fetch: typeof fetch }; } // We attach the Cloudflare `fetch()` handler to the global scope // so that we can export it when we process the Angular output. // See tools/bundle.mjs async function workerFetchHandler(request: Request, env: Env) { const url = new URL(request.url); console.log("render SSR", url.href); // Get the root `index.html` content. const indexUrl = new URL("/", url); const indexResponse = await env.ASSETS.fetch(new Request(indexUrl)); const document = await indexResponse.text(); const content = await renderApplication(bootstrap, { document, url: url.pathname, }); // console.log("rendered SSR", content); return new Response(content, indexResponse); } export default { fetch: (request: Request, env: Env) => (globalThis as any)["__zone_symbol__Promise"].resolve( workerFetchHandler(request, env) ), };
上述代码使用renderApplication返回字符串输出,我想了解如何从Angular应用传递HTTP状态码(如404页面未找到)或自定义请求特定头信息。我已查看过Stack Overflow上的相关链接,但不确定在Cloudflare Pages/Worker环境下该如何实现。
解决方案
1. 传递HTTP状态码(如404)
通过Angular的TransferState在组件与Cloudflare Worker之间传递状态标记,再在Worker中修改响应状态码:
步骤1:在Angular组件中标记状态
在404等错误组件中,注入TransferState并设置状态码:
// src/app/not-found/not-found.component.ts import { Component, OnInit } from '@angular/core'; import { TransferState, makeStateKey } from '@angular/platform-browser'; const STATUS_CODE_KEY = makeStateKey<number>('statusCode'); @Component({ selector: 'app-not-found', templateUrl: './not-found.component.html', }) export class NotFoundComponent implements OnInit { constructor(private transferState: TransferState) {} ngOnInit() { this.transferState.set(STATUS_CODE_KEY, 404); } }
步骤2:在Worker中读取并设置响应码
修改server.ts中的workerFetchHandler,创建TransferState实例并传入渲染配置,最后读取状态码修改响应:
import { renderApplication, ɵSERVER_CONTEXT } from "@angular/platform-server"; import bootstrap from "./src/main.server"; import { TransferState, makeStateKey } from '@angular/platform-browser'; const STATUS_CODE_KEY = makeStateKey<number>('statusCode'); interface Env { ASSETS: { fetch: typeof fetch }; } async function workerFetchHandler(request: Request, env: Env) { const url = new URL(request.url); console.log("render SSR", url.href); const indexUrl = new URL("/", url); const indexResponse = await env.ASSETS.fetch(new Request(indexUrl)); const document = await indexResponse.text(); // 创建TransferState实例 const transferState = new TransferState(); const content = await renderApplication(bootstrap, { document, url: url.pathname, providers: [ { provide: TransferState, useValue: transferState }, { provide: ɵSERVER_CONTEXT, useValue: 'cloudflare-worker' } ] }); // 获取状态码,默认200 const statusCode = transferState.get(STATUS_CODE_KEY, 200); // 复制原响应头并修改状态码 const responseHeaders = new Headers(indexResponse.headers); return new Response(content, { status: statusCode, headers: responseHeaders }); } export default { fetch: (request: Request, env: Env) => (globalThis as any)["__zone_symbol__Promise"].resolve( workerFetchHandler(request, env) ), };
2. 传递自定义请求头
同样借助TransferState传递头信息,再在Worker中添加到响应:
步骤1:在Angular组件中传递自定义头
// src/app/some-component/some-component.ts import { Component, OnInit } from '@angular/core'; import { TransferState, makeStateKey } from '@angular/platform-browser'; const CUSTOM_HEADERS_KEY = makeStateKey<Record<string, string>>('customHeaders'); @Component({ selector: 'app-some-component', templateUrl: './some-component.component.html', }) export class SomeComponent implements OnInit { constructor(private transferState: TransferState) {} ngOnInit() { this.transferState.set(CUSTOM_HEADERS_KEY, { 'X-Custom-Header': 'Cloudflare-Worker-Angular', 'X-Content-Type-Options': 'nosniff' }); } }
步骤2:在Worker中添加自定义头到响应
修改workerFetchHandler,读取自定义头并添加到响应:
async function workerFetchHandler(request: Request, env: Env) { // ... 其他代码 ... const transferState = new TransferState(); const content = await renderApplication(bootstrap, { document, url: url.pathname, providers: [ { provide: TransferState, useValue: transferState }, { provide: ɵSERVER_CONTEXT, useValue: 'cloudflare-worker' } ] }); const statusCode = transferState.get(STATUS_CODE_KEY, 200); const customHeaders = transferState.get(CUSTOM_HEADERS_KEY, {}); const responseHeaders = new Headers(indexResponse.headers); // 批量添加自定义头 Object.entries(customHeaders).forEach(([key, value]) => { responseHeaders.set(key, value); }); return new Response(content, { status: statusCode, headers: responseHeaders }); }
备选方案:直接捕获路由错误
如果不需要渲染错误组件,可在路由守卫中抛出带状态码的错误,再在Worker中捕获:
// src/app/app-routing.module.ts import { NgModule } from '@angular/core'; import { RouterModule, Routes, CanActivateFn } from '@angular/router'; import { NotFoundComponent } from './not-found/not-found.component'; const authGuard: CanActivateFn = (route, state) => { if (!someCondition) { throw { status: 403, message: 'Forbidden' }; } return true; }; const routes: Routes = [ // ... 其他路由 ... { path: '**', component: NotFoundComponent }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
在server.ts中捕获错误并设置响应:
async function workerFetchHandler(request: Request, env: Env) { // ... 其他代码 ... let statusCode = 200; let content; try { content = await renderApplication(bootstrap, { document, url: url.pathname, }); } catch (err: any) { if (err.status) { statusCode = err.status; // 可加载预定义的错误页面 const errorIndexResponse = await env.ASSETS.fetch(new Request('/404.html')); content = await errorIndexResponse.text(); } } const responseHeaders = new Headers(indexResponse.headers); return new Response(content, { status: statusCode, headers: responseHeaders }); }
内容的提问来源于stack exchange,提问作者Naveed Ahmed
相关产品推荐
相关产品推荐

