Django API与Flutter-web前端集成时CSRF令牌传递及CORS相关问题求助
我有一个基于Python/Django的Web API,只有一个端点/api/v1/form,由Flutter-web前端调用。目前我用禁用CSRF令牌验证的配置可以正常运行,相关配置如下:
requirements.txt
Django==5.1.7 django-cors-headers==4.7.0
webserver/settings.py
... ALLOWED_HOSTS = ["localhost"] CORS_ALLOWED_ORIGINS = ["http://localhost:8001"] # flutter dev port INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'corsheaders', 'webserver', ] ... MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ...
webserver/urls.py
from django.urls import path import webserver.views urlpatterns = [ path('api/v1/form', webserver.views.api_v1_form, name="api_v1_form"), ] ...
webserver/views.py
from django.http import HttpResponse, HttpResponseBadRequest def api_v1_form(request): if request.method == "POST": process_request(request.body) return HttpResponse("request successfully processed.") return HttpResponseBadRequest("Expected a POST request")
flutter/lib/page_form.dart
Future<int> sendForm(MyData data) async { final response = await http.post( Uri.parse("http://localhost:8000/api/v1/form"), body: data.toString(), ); return response.statusCode; }
我的第一个疑问
这里我搞不懂的是:如果禁用CORS包,只用原生Django服务器,我能发送请求到API,但收不到响应,这是为什么?
尝试配置CSRF令牌的过程
下面是我尝试获取并在请求中使用CSRF令牌的配置步骤:
第一步:修改settings.py
ALLOWED_HOSTS = ["localhost"] CSRF_TRUSTED_ORIGINS = ["http://localhost:8001", "http://localhost:8000"] INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'webserver', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.common.CommonMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
只修改settings.py后,我收到403 forbidden响应。
第二步:给视图添加ensure_csrf_cookie装饰器
webserver/views.py
from django.views.decorators.csrf import ensure_csrf_cookie @ensure_csrf_cookie def api_v1_form(request): ... # 其余代码不变
但还是收到403 forbidden响应。
第三步:新增获取令牌的API端点
我尝试新增一个/api/v1/token API来获取令牌,并在请求中复用它:
webserver/views.py
def api_v1_token(request): if request.method == "GET": return HttpResponse(get_token(request)) return HttpResponseBadRequest() @ensure_csrf_cookie def api_v1_form(request): if request.method == "OPTIONS": # POST -> OPTIONS # ...
flutter/lib/page_form.dart
Future<int> envoyerFormulaire(MyData data) async { final tokenResponse = await http.get( Uri.parse("$apiUrlBase/token"), ); final token = tokenResponse.body.toString(); Uri uriPost = Uri.parse("$apiUrlBase/form"); final dataResponse = await http.post( uriPost, body: data.toString(), headers: { "X-CSRFToken": token, } ); return dataResponse.statusCode; }
但通过浏览器检查器,我发现获取令牌时出现错误,提示缺少Access-Control-Allow-Origin头。于是我修改了视图:
webserver/views.py
def api_v1_token(request): if request.method == "GET": response = HttpResponse(get_token(request)) response["Access-Control-Allow-Origin"] = "http://localhost:8001" return response return HttpResponseBadRequest() def api_v1_form(request: HttpResponse): if request.method == "OPTIONS": # ... response["Access-Control-Allow-Origin"] = "http://localhost:8001" return response
现在我能获取到令牌了,但POST请求又出现PreflightMissingAllowOriginHeader错误。我明明已经给所有请求都加了Access-Control-Allow-Origin头,这是为什么?
而且我发现POST请求会触发两个请求:预检(preflight)和实际请求。预检请求返回500 internal错误,因为Django尝试解析空请求的内容。我通过添加过滤逻辑修复了这个问题:
webserver/views.py
if request.method == "OPTIONS": # 预检请求 res = HttpResponse() res["Access-Control-Allow-Origin"] = "http://localhost:8001" return res if request.method == "GET": # 后续正常处理逻辑
现在预检请求返回200 OK,但实际请求又出现CORS错误:HeaderDisallowedByPreflightResponse。
我现在的困惑
我现在卡住了,感觉自己对CORS头和请求的理解有误区:
- 是不是我遗漏了某些必要的头?应该设置什么值?
- 是不是应该在预检请求中处理数据,然后通过后续的GET请求返回结果,而不是用单个POST请求?这是预期的行为吗?
备注:内容来源于stack exchange,提问作者SpaceBurger

