【问题标题】:POST API response blocked by CORS policy - React and Django Rest FramworkPOST API 响应被 CORS 策略阻止 - React 和 Django Rest Framework
【发布时间】:2019-10-13 00:49:02
【问题描述】:

我在 Django 中使用 Django Rest Framework 和 React 前端创建了一个后端服务器。我的前面通过 API 从后面检索数据。每个应用程序都位于同一域的不同子域中。我用 Cloudflare 管理 DNS 和 SSL / 安全。

我对 GET 调用没有任何问题。对于 POST 调用,我通过表单将 POST 数据发送到服务器,并且我知道它可以工作,因为数据库发生了更改(在此实例中创建了记录)。但是,我已经使用 axios 和 polly-js 实现了一个“重试直到”功能。此方法一直等待,直到收到 201 CREATED 响应,否则重试。

我的问题是,当我在 React 上提交表单时,我的后端服务器确实接收并处理了 POST,但响应被阻止。所以 10-15 秒后,我通过控制台收到一条错误消息,我的“重试直到”方法发送另一个 POST 请求。第二个的响应没有被 Chrome 阻止,我收到 201 状态。但总体效果是我现在在数据库中有 2 条相同的记录,因为第一次调用没有“收到”响应并重试。

我得到的控制台错误是:

Access to XMLHttpRequest at 'https://subdomain.domain.io/' from origin 'https://api.domain.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. 

我已经做过和没做过的事情:

  • 我不认为这是一个后端问题,因为 POST 通过并创建了记录。但我已将 Django 中的所有 CORS 来源列入白名单
  • 我已通过 axios 在我的 POST 请求中添加了标头 'Access-Control-Allow-Origin': '*'
  • 我从我的 Django DRF 响应中手动添加了相同的 'Access-Control-Allow-Origin': '*' 标头。

我发送的两个请求(第一个通过表单提交,第二个通过自动重试)是相同的(通过 Chrome 网络选项卡查看):

Accept: application/json, text/plain, */*
Content-Type: application/json;charset=UTF-8
Origin: https://subdomain.domain.io
Referer: https://subdomain.domain.io/path
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36

我的 POST 和重试方法:

const postData = (url, data, headers) => {

    headers['Access-Control-Allow-Origin'] = "*"

    return polly()
      .waitAndRetry([100, 200, 400, 1000])
      .executeForPromise(async () => {
        const rsp = await axios.post(url, data, headers);

        if (rsp.status < 210) {

          return rsp.data;

        }

        return Promise.reject(rsp);
      });
  };

第二次尝试成功时得到的响应:

access-control-allow-origin: *
allow: GET, POST, HEAD, OPTIONS
cf-ray: 4dd7cbccce256948-CDG
content-length: 364
content-type: application/json
date: Mon, 27 May 2019 11:54:47 GMT
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
status: 201
strict-transport-security: max-age=2592000; includeSubDomains; preload
vary: Accept, Origin
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN

作为参考,Django 中的 CORS 设置

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django_otp.middleware.OTPMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]


CORS_ORIGIN_ALLOW_ALL = True


CORS_ALLOW_HEADERS = (
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'access-control-allow-origin'
)

编辑

Firefox 向我显示第一个 POST 请求的 504 GATEWAY TIMEOUT 的响应:

cf-ray: 4dd82ac15f42cd97-CDG
content-type: text/html; charset=UTF-8
date: Mon, 27 May 2019 13:00:36 GMT
expect-ct: max-age=604800, report-uri="ht….com/cdn-cgi/beacon/expect-ct"
expires: Thu, 01 Jan 1970 00:00:01 GMT
pragma: no-cache
server: cloudflare
set-cookie: __cfduid=d0a3a9ee872171ada14cb…n=.wisly.io; HttpOnly; Secure
set-cookie: cf_use_ob=0; path=/; expires=Mon, 27-May-19 13:01:06 GMT
strict-transport-security: max-age=2592000; includeSubDomains; preload
x-content-type-options: nosniff
X-Firefox-Spdy: h2

缺少 Access-Control-Allow-Origin,但它是我的后端代码的一部分。 Cloudflare 会发生什么事情吗?

预期的结果是,当我通过表单 POST 时,收到 201 回复(Chrome 会接受并读取),以便我可以

  • 向用户显示表单已正确保存到数据库中
  • 不重试 POST,导致双重进入。

谢谢!

【问题讨论】:

  • 不要在 Axios 中添加access-control-allow-origin——它不是客户端标头。
  • 谢谢,确实如此。尽管看起来,删除它并不能解决问题。
  • 您必须首先为 OPTIONS 请求提供正确的响应,请参阅网络标签,谷歌提示
  • 如果您看到 No 'Access-Control-Allow-Origin' header is present 消息,响应的 HTTP 状态代码是什么?使用浏览器开发工具中的网络窗格进行检查。是 4xx 还是 5xx 错误而不是 2xx 成功消息?
  • @xadm 啊!实际上,这可能是第一个请求没有发送 OPTIONS 的问题。我怎么能“强迫”这个?其他请求之前会自动发送 OPTIONS。我的代码中有一个错字,我已将其更改为检查低于 210 的响应,因此选项的 200 确实有效。

标签: django reactjs google-chrome django-rest-framework http-status-code-504


【解决方案1】:

您无需将access-control-allow-origin 放入您的CORS_ALLOW_HEADERS
Django CORS 将根据您的配置自动添加请求。

您可以尝试一些技巧:

  • 使用default_headers 而不是放置所有默认标头(只是为了改进代码)
  • Chrome 发送的一些基本标头已经存在问题,而我的应用程序不允许这些标头,因此我必须添加
from corsheaders.defaults import default_headers

CORS_ALLOW_HEADERS = default_headers + (
    'Cache-Control', 'If-Modified-Since',
)
  • 尝试添加“可信来源”
CSRF_TRUSTED_ORIGINS = (
    '*.yourdomain.com',
)

【讨论】:

  • 感谢您的提示!我尝试了所有这些,但仍然有同样的问题。实际上 Safari 和 Firefox 具有相同的行为。一旦“第一个”请求通过,其余的就会正常工作。通常我需要关闭浏览器并清除缓存才能再次发生问题。
【解决方案2】:

查看这个库。 https://pypi.org/project/django-cors-headers。它帮助我用 React 解决了同样的问题。

【讨论】:

  • 谢谢!我已经安装了它,不幸的是即使使用这个包也存在问题。
猜你喜欢
  • 1970-01-01
  • 2021-02-07
  • 2020-05-28
  • 2019-08-17
  • 1970-01-01
  • 2021-12-25
  • 2020-07-03
  • 1970-01-01
  • 2021-05-12
相关资源
最近更新 更多