【发布时间】: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