Angular Universal服务端HTTP请求出现CORS错误
这个问题我之前碰到过,核心原因其实很清晰:CORS是浏览器的安全机制,但服务端渲染时Node.js发起的HTTP请求,它的Origin会变成服务器的IP+端口(比如http://<server ip>:<port>),而你的API服务器只允许www.mysite.com的Origin,所以被拦截了。另外,浏览器环境下HttpClient不允许手动设置Origin头(这是浏览器的安全限制),所以直接用拦截器改Origin会报错。下面给你几个可行的解决方案:
方案1:区分环境,仅在服务端渲染时设置Origin头
我们可以利用Angular的PLATFORM_ID来判断当前是浏览器还是服务端环境,只在服务端修改请求的Origin头,浏览器环境下保持原样,这样既符合API的CORS规则,又不会触发浏览器的安全错误。
步骤:
- 创建一个HTTP拦截器:
import { Injectable, Inject, PLATFORM_ID } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { isPlatformServer } from '@angular/common'; import { Observable } from 'rxjs'; @Injectable() export class ServerOriginInterceptor implements HttpInterceptor { constructor(@Inject(PLATFORM_ID) private platformId: Object) {} intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { // 仅在服务端渲染时修改Origin if (isPlatformServer(this.platformId)) { const modifiedRequest = req.clone({ setHeaders: { 'Origin': 'https://www.mysite.com' } }); return next.handle(modifiedRequest); } // 浏览器环境下直接返回原请求 return next.handle(req); } }
- 在根模块中注册这个拦截器:
打开app.module.ts,在providers数组里添加:
import { HTTP_INTERCEPTORS } from '@angular/common/http'; // ... providers: [ { provide: HTTP_INTERCEPTORS, useClass: ServerOriginInterceptor, multi: true } ]
这样配置后,服务端渲染时的请求会带上正确的www.mysite.com Origin,API服务器就会允许访问;浏览器环境下请求的Origin还是原来的www.mysite.com,也不会有问题。
方案2:用Angular Universal服务器做代理转发
另一种更稳妥的方式是让Universal服务器作为代理,转发所有API请求到你的api.mysite.com,这样服务端发起的请求是从Universal服务器到API服务器,我们可以在代理时统一设置Origin头,同时还能隐藏API的真实地址。
步骤:
- 安装代理中间件:
npm install http-proxy-middleware --save
- 在
server.ts中配置代理:
import { createProxyMiddleware } from 'http-proxy-middleware'; // 在Express app初始化后添加代理配置 app.use('/api', createProxyMiddleware({ target: 'https://api.mysite.com', changeOrigin: true, // 设置正确的Origin头 headers: { 'Origin': 'https://www.mysite.com' }, // 如果你的API路径不需要/api前缀,就重写路径 pathRewrite: { '^/api': '' } }));
- 修改Angular中的请求地址:
把原来的https://api.mysite.com/xxx改成/api/xxx,比如:
// 原来的请求 this.http.get('https://api.mysite.com/user'); // 修改后 this.http.get('/api/user');
这样,服务端渲染时请求会被Universal服务器代理到API,同时带上正确的Origin;浏览器环境下,你可以通过proxy.conf.json配置开发环境的代理,生产环境下如果是同域名部署(或者CDN配置),也能正常访问。
方案3:修改API服务器的CORS配置(如果API是你可控的)
如果你的API服务器是自己维护的,可以直接修改CORS规则,允许服务端渲染服务器的Origin,或者通过IP白名单来信任服务端的请求:
示例(假设API用Express和cors中间件):
const cors = require('cors'); const corsOptions = { // 允许浏览器和服务端的Origin origin: ['https://www.mysite.com', 'http://<server-ip>:<port>'], credentials: true }; app.use(cors(corsOptions));
不过这个方案的缺点是,如果服务端的IP发生变化,你需要手动更新CORS配置,灵活性不如前两个方案,适合固定IP的场景。
内容的提问来源于stack exchange,提问作者Shay Friedman

