Angular+Django项目中OPTIONS请求的CORS头配置问题
解决Angular+Django跨域CORS及OPTIONS预检请求问题
核心问题原因
手动给单个GET响应添加CORS头的方式无法覆盖浏览器自动发送的OPTIONS预检请求,Django默认会处理OPTIONS请求但未携带所需的CORS头,导致POST请求被拦截。
推荐解决方案:使用django-cors-headers库
这是Django生态中处理CORS最便捷且规范的方式,无需手动编写中间件:
- 安装库
pip install django-cors-headers - 修改
settings.py配置- 将库添加到
INSTALLED_APPS:INSTALLED_APPS = [ # ... 其他已有应用 'corsheaders', ] - 将CORS中间件添加到
MIDDLEWARE(注意顺序要在CommonMiddleware之前):MIDDLEWARE = [ # ... 其他已有中间件 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', ] - 配置允许的前端源(替换为你的Angular服务地址,比如
http://localhost:4200):CORS_ALLOWED_ORIGINS = [ "http://localhost:4200", ] - 开启允许携带凭证(对应前端的
withCredentials: true):CORS_ALLOW_CREDENTIALS = True
- 将库添加到
配置完成后,所有请求(包括OPTIONS预检、GET、POST)都会自动带上正确的CORS头,无需手动处理响应。
自定义中间件方案(不依赖第三方库)
如果不想使用第三方库,可以自己编写全局中间件统一处理所有请求的CORS头:
- 在你的Django应用下创建
middleware.py文件,添加以下代码:from django.utils.deprecation import MiddlewareMixin class CORSMiddleware(MiddlewareMixin): def process_response(self, request, response): # 替换为你的Angular服务地址 response["Access-Control-Allow-Origin"] = "http://localhost:4200" response["Access-Control-Allow-Credentials"] = "true" # 处理OPTIONS预检请求,允许所需的方法和头 if request.method == "OPTIONS": response["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS" response["Access-Control-Allow-Headers"] = "Content-Type, X-CSRFToken" return response - 在
settings.py的MIDDLEWARE中添加该中间件(同样放在CommonMiddleware之前):MIDDLEWARE = [ # ... 其他已有中间件 'your_app_name.middleware.CORSMiddleware', 'django.middleware.common.CommonMiddleware', ]
额外注意事项
- Angular端确保所有需要携带Cookie的请求都设置
withCredentials: true,比如POST请求:this.http.post(this.url, requestData, { withCredentials: true }); - 处理CSRF令牌:Django的CSRF保护要求POST请求携带
X-CSRFToken头,可通过Angular拦截器自动实现:
在import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http'; @Injectable() export class CsrfInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler) { // 从Cookie中获取csrftoken const csrfToken = document.cookie.split('; ') .find(row => row.startsWith('csrftoken='))?.split('=')[1]; if (csrfToken) { req = req.clone({ headers: req.headers.set('X-CSRFToken', csrfToken) }); } return next.handle(req); } }app.module.ts中注册拦截器:import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { CsrfInterceptor } from './csrf.interceptor'; @NgModule({ // ... 其他配置 providers: [ { provide: HTTP_INTERCEPTORS, useClass: CsrfInterceptor, multi: true } ] }) export class AppModule { }
内容的提问来源于stack exchange,提问作者bp-pet
相关产品推荐
相关产品推荐

