一、 跨域问题:前端开发者的“拦路虎”

当我们用Angular开发应用时,经常会遇到一个让人头疼的情况:你的应用运行在 http://localhost:4200,而你需要调用的API服务却在另一个地址,比如 http://api.yourcompany.com。当你尝试从自己的应用去请求这个API时,浏览器控制台就会抛出一个红彤彤的错误,告诉你请求被“同源策略”阻止了。

这就是“跨域”问题。简单来说,浏览器出于安全考虑,默认禁止一个网页的脚本去请求和它不同“源”(协议、域名、端口任一不同)的资源。这就像你家小区的门禁,只认本小区的住户(同源),外来访客(跨域)需要登记(CORS)或者由业主(代理)带进去。

在Angular开发中,我们主要有两种“登记”或“带入”的方式来解决它:开发环境下的代理配置生产环境下的CORS处理。下面我们就来详细聊聊这两种方法。

二、 开发利器:Angular CLI代理配置详解

在开发阶段,我们最常用、最方便的方法就是配置代理。它的原理很简单:让Angular自带的开发服务器(ng serve)充当一个中间人。你所有的请求都先发给这个开发服务器(同源,所以不会跨域),然后由它转发给真正的后端API服务器,拿到结果后再返回给你。这样,浏览器眼里看到的始终是同源请求,跨域问题就消失了。

技术栈声明:本文所有示例均基于 Angular CLI + Node.js/Express 后端环境。

1. 创建代理配置文件 在你的Angular项目根目录下(与 angular.json 同级),创建一个名为 proxy.conf.json 的文件。这个文件就是我们的代理规则说明书。

{
  "/api": { // 规则1:所有以 `/api` 开头的请求
    "target": "http://localhost:3000", // 目标:转发到本地的Node.js后端
    "secure": false, // 如果目标是https,则需要证书验证。本地http开发设为false即可
    "changeOrigin": true, // 强烈建议设置为true。它会改变请求头中的`Host`字段为目标地址,有些后端服务会校验这个
    "logLevel": "debug", // 可选:在终端显示代理日志,调试时非常有用
    "pathRewrite": { // 可选:路径重写。这里意味着请求`/api/users`会被转发为`/users`
      "^/api": ""
    }
  },
  "/weather": { // 规则2:所有以 `/weather` 开头的请求
    "target": "https://api.openweathermap.org", // 也可以转发到外部公共API
    "secure": true, // 目标是https,设为true
    "changeOrigin": true,
    "headers": { // 可选:可以添加或覆盖请求头,比如添加API密钥
      "Authorization": "Bearer your_api_key_here"
    }
  }
}

2. 在Angular CLI中启用代理 创建好配置文件后,我们需要告诉 ng serve 使用它。有两种方式:

  • 方式一:修改 package.json 脚本(推荐) 打开 package.json 文件,找到 scripts 下的 start 命令,修改它:

    "scripts": {
      "start": "ng serve --proxy-config proxy.conf.json",
      // ... 其他脚本
    }
    

    之后,你只需要运行 npm startyarn start,代理就自动生效了。

  • 方式二:命令行直接指定 你也可以在每次启动时手动指定:

    ng serve --proxy-config proxy.conf.json
    

3. 在Angular服务中发起请求 配置好后,你在Angular服务中的代码就可以像访问同源接口一样写地址了。

// 技术栈:Angular (TypeScript)
// 文件:src/app/services/data.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class DataService {

  constructor(private http: HttpClient) { }

  // 根据代理配置,这个请求会被发送到 `http://localhost:4200/api/users`
  // 然后被代理服务器转发到 `http://localhost:3000/users` (因为配置了pathRewrite)
  getUsers(): Observable<any> {
    return this.http.get('/api/users');
  }

  // 这个请求会被转发到 `https://api.openweathermap.org/data/2.5/weather?q=London`
  getWeather(city: string): Observable<any> {
    return this.http.get(`/weather/data/2.5/weather?q=${city}`);
  }
}

代理配置的优点:

  • 对前端代码无侵入:你不需要修改任何请求的URL逻辑,开发和生产环境可以轻松切换。
  • 避免CORS预检请求:对于复杂的请求(如带自定义头、Content-Type非简单值),浏览器会先发一个OPTIONS预检请求,代理可以绕过这个步骤,提升开发效率。
  • 功能强大:支持路径重写、请求头修改、日志等,非常灵活。

注意事项:

  • 仅用于开发ng serve 的代理功能只在开发服务器运行时有效。构建后的静态文件(ng build)不包含此功能。
  • 配置热更新:修改 proxy.conf.json 后,通常需要重启 ng serve 才能生效。

三、 生产必备:后端CORS处理指南

当你的Angular应用开发完成,使用 ng build 构建并部署到服务器(例如Nginx)后,代理配置就失效了。此时,解决跨域问题的责任就完全落在了后端API服务器身上。这就需要后端服务正确配置CORS(跨源资源共享)策略。

CORS是一种W3C标准,它允许服务器声明哪些“外源”可以访问自己的资源。后端通过设置一系列HTTP响应头来实现。

1. 在后端服务器配置CORS 我们以Node.js的Express框架为例,展示如何配置。

// 技术栈:Node.js + Express
// 文件:server.js (后端API服务器入口文件)

const express = require('express');
const app = express();
// 1. 引入cors中间件(最简便的方式)
const cors = require('cors');

// 2. 使用cors中间件,并可以进行详细配置
app.use(cors({
  origin: 'https://www.your-angular-app.com', // 允许访问的源。生产环境应替换为你的前端域名
  // origin: ['https://site-a.com', 'https://site-b.com'], // 也可以配置多个源
  // origin: '*', // 【慎用】允许所有源。仅用于测试或完全公开的API
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 允许的HTTP方法
  allowedHeaders: ['Content-Type', 'Authorization'], // 允许的请求头
  credentials: true, // 是否允许发送Cookie等凭证。如果设为true,则`origin`不能为`*`
  maxAge: 86400 // 预检请求结果的缓存时间(秒)
}));

// 3. 如果你的请求涉及凭证(如cookies、HTTP认证),前端也需要配合
// 在Angular的HttpClient请求中,需要设置 `withCredentials: true`
// this.http.get(apiUrl, { withCredentials: true });

// 你的API路由
app.get('/users', (req, res) => {
  res.json([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]);
});

app.post('/data', express.json(), (req, res) => {
  // 处理数据...
  res.json({ message: 'Data received!' });
});

app.listen(3000, () => console.log('API server running on port 3000'));

2. 手动设置CORS响应头(不依赖中间件) 你也可以手动在路由或全局中间件中设置这些头部,以更深入地理解其原理。

// 技术栈:Node.js + Express
// 文件:server.js (手动设置CORS头示例)

app.use((req, res, next) => {
  // 设置允许访问的源
  res.header('Access-Control-Allow-Origin', 'https://www.your-angular-app.com');
  // 设置允许的请求头
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
  // 设置允许的请求方法
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  // 设置是否允许携带凭证
  res.header('Access-Control-Allow-Credentials', 'true');

  // 处理OPTIONS预检请求
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200); // 对预检请求直接返回成功
  }
  next(); // 继续处理后续请求
});

CORS处理的优点:

  • 标准且安全:是W3C官方标准,允许后端精细控制资源访问权限。
  • 生产环境唯一解:是部署后解决跨域问题的正确方式。

注意事项:

  • 后端责任:需要后端开发人员理解和正确配置。
  • 预检请求:对于“非简单请求”,浏览器会先发送OPTIONS预检请求,后端必须正确处理并返回正确的CORS头,否则实际请求会被阻止。
  • Origin慎用*:在生产环境中,将 Access-Control-Allow-Origin 设置为 *(通配符)虽然方便,但意味着任何网站都可以前端调用你的API,存在安全风险。应明确指定允许的前端域名。

四、 场景、选择与总结

应用场景分析:

  • 纯前端开发阶段:你正在独立开发Angular前端,后端API由其他团队提供或使用模拟数据。此时,代理配置是最佳选择。它让你能无缝对接任何后端地址,无需等待后端配置CORS,极大提升开发体验和效率。
  • 全栈开发或可控后端:你同时负责前后端,或者后端也在你的掌控之中。你可以在开发初期使用代理,但同时尽早为后端配置好CORS(特别是开发环境允许本地前端地址http://localhost:4200)。这有助于保持开发环境与生产环境的一致性。
  • 应用部署上线后:当Angular应用被构建并部署到像Nginx这样的Web服务器上时,必须由后端API服务器配置CORS来解决跨域问题。此时代理配置已完全无用。

技术选择与总结:

  1. 开发环境,优先使用代理:这是Angular CLI提供的强大开发工具,能让你专注于前端逻辑,免受跨域干扰。记住,它只是开发服务器的一个转发功能。
  2. 生产环境,必须依赖CORS:这是HTTP层面的标准解决方案。你需要与后端同事协作,确保API服务器正确设置了 Access-Control-Allow-Origin 等响应头。
  3. 不要混淆两者:永远不要试图在构建后的生产包里去寻找代理配置的功能;也尽量不要在开发阶段因为后端没配CORS而烦恼,直接用代理绕过即可。
  4. 终极心法:理解“跨域”是浏览器的安全限制。代理是“骗过”浏览器,让请求看起来是同源的;CORS是“告知”浏览器,告诉它这个跨源请求是被服务器允许的。前者是开发工具,后者是生产标准。

掌握这两种方法,你就能在Angular项目开发的全生命周期中,游刃有余地解决跨域这个“拦路虎”,让前后端通信畅通无阻。