You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django API与Flutter-web前端集成时CSRF令牌传递及CORS相关问题求助

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 19:54:40