Angular中拦截isr/uuidv4请求,添加timeZone查询参数再转发至服务器
Angular ISR 自动添加时区查询参数问题解决
问题背景
需求:用户访问isr/uuidv4路径时,自动添加timeZone查询参数,最终请求路径应为isr/uuidv4?timeZone=Asia/Jakarta(动态获取客户端时区)。
尝试用CanMatchFn守卫实现,但守卫在服务端与客户端各执行一次:服务端环境下Intl.DateTimeFormat().resolvedOptions().timeZone返回undefined,导致服务端守卫返回false,拒绝渲染;客户端获取空HTML后才重定向到带参数路径,只能触发客户端渲染,不符合ISR服务端渲染的预期。
预期流程
- 用户访问
isr/uuidv4 - 被重定向至
isr/uuidv4?timeZone=Asia/Jakarta - 服务器渲染该带参数的路径
实际流程
- 用户访问
isr/uuidv4 - 服务端守卫返回
false,服务器不渲染 - 用户获取空HTML后被重定向至
isr/uuidv4?timeZone=Asia/Jakarta - 客户端渲染页面
解决方案
方案1:调整守卫逻辑,服务端允许渲染,客户端处理重定向
修改守卫,服务端即使无timeZone参数也返回true,让服务端先渲染基础页面,客户端在浏览器环境下完成重定向并触发ISR:
import { isPlatformServer } from '@angular/common'; import { PLATFORM_ID, inject } from '@angular/core'; import { CanMatchFn, Router } from '@angular/router'; export const uUIDv4TimeZoneGuard: CanMatchFn = (route, segments) => { const router = inject(Router); const queryParamMap = router.getCurrentNavigation()?.extractedUrl.queryParamMap; const platformID = inject(PLATFORM_ID); const isServer = isPlatformServer(platformID); if (!queryParamMap?.has('timeZone')) { if (!isServer) { // 客户端环境下重定向,替换历史记录避免重复跳转 router.navigate( ['isr/uuidv4'], { queryParams: { timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone }, replaceUrl: true } ); } // 服务端允许路由匹配,确保能渲染基础页面 return true; } return true; };
方案2:通过Cookie传递时区,服务端直接重定向触发ISR
如果需要服务端直接渲染带参数的页面,可通过Cookie传递客户端时区,让服务端完成重定向:
- 客户端写入时区Cookie
在应用初始化时写入客户端时区到Cookie:
// app.config.ts import { APP_INITIALIZER } from '@angular/core'; const setTimeZoneCookie = () => { return () => { const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; document.cookie = `timeZone=${timeZone}; path=/; max-age=31536000; SameSite=Lax`; }; }; export const appConfig = { providers: [ { provide: APP_INITIALIZER, useFactory: setTimeZoneCookie, multi: true } ] };
- 服务端读取Cookie并重定向
在Angular Universal的server.ts中添加路由拦截逻辑:
// server.ts import { ngExpressEngine } from '@nguniversal/express-engine'; import express from 'express'; import cookieParser from 'cookie-parser'; import { join } from 'path'; import { AppServerModule } from './src/main.server'; const app = express(); app.use(cookieParser()); // 引入cookie解析中间件 app.get('*', (req, res, next) => { const targetPath = '/isr/uuidv4'; if (req.originalUrl.startsWith(targetPath) && !req.query.timeZone) { // 读取Cookie中的时区,默认值设为Asia/Jakarta const timeZone = req.cookies.timeZone || 'Asia/Jakarta'; // 302重定向到带参数的路径,触发ISR渲染 return res.redirect(302, `${targetPath}?timeZone=${timeZone}`); } next(); }); // 剩余Universal配置代码...
核心原因说明
服务端环境(如Angular Universal)无法访问浏览器的Intl API,直接在服务端守卫中获取客户端时区会返回undefined。原守卫在服务端返回false导致服务端拒绝渲染,只能由客户端完成重定向。调整后的逻辑要么让服务端先渲染基础页面,客户端补充参数;要么通过Cookie传递时区,让服务端直接重定向到带参数路径,从而触发ISR服务端渲染,符合预期流程。
内容的提问来源于stack exchange,提问作者Jason Rich Darmawan
相关产品推荐
相关产品推荐

