为啥 Flask-Cors 在生产中没有检测到我的跨域域?
Posted
技术标签:
【中文标题】为啥 Flask-Cors 在生产中没有检测到我的跨域域?【英文标题】:Why is Flask-Cors not detecting my Cross-Origin domain in production?为什么 Flask-Cors 在生产中没有检测到我的跨域域? 【发布时间】:2021-04-11 17:21:59 【问题描述】:我的网站前后端是分开的,所以我的后端服务器需要开放CORS权限,这样前端才能向它请求数据。
我在开发中使用Flask-Cors成功,但是当我部署到生产环境时它不起作用。 (请注意,我已经查看了关于 SO 的其他烧瓶-cors 问题,但没有一个适合我的情况)
以下是正在工作开发的相关代码:
# 3rd party imports
import flask
from flask import Flask, request, redirect, send_from_directory, jsonify
from flask_cors import CORS
# Create the app
app = Flask(__name__)
CORS(app, origins=[
'http://localhost:5001',
])
# Define the routes
@app.route('/')
def index():
# no CORS code was necessary here
app.logger.info(f'request is: flask.request')
我尝试过的:
将我的服务器的 IP 地址'http://162.243.168.182:5001'
添加到 CORS 列表中不足以解决问题,尽管我知道它应该在那里。
似乎使用'*'
允许所有来源不 工作。 (非常可疑!)
请注意,我使用的是 Docker 容器,因此我在开发和生产之间的环境几乎相同。但不同的是我在不同的服务器上,并且我修改了前端以将请求发送到新的 IP 地址(导致著名的“Access-Control-Allow-Origin” header missing
CORS 错误)。
现在我想知道 flask.request
对象是否以某种方式丢失了信息,这导致 Flask-Cors 无法像预期的那样发送 Access-Control-Allow-Origin 标头。如果您认为有帮助,我可以提供该日志记录信息!
更多信息!
我在 PROD 中使用的 Dockerfile 是:
# base image
FROM tiangolo/uwsgi-nginx-flask:python3.8-2020-12-19
# install deps
RUN pip3 install ediblepickle==1.1.3
# RUN pip3 install flask==1.1.2 # pre-installed on tiangolo/uwsgi-nginx-flask
RUN pip3 install flask-cors==3.0.9
RUN pip3 install numpy==1.19.2
RUN pip3 install scipy==1.5.2
RUN pip3 install pandas==1.1.2
RUN pip3 install networkx==2.5
# pull in files for deployment
COPY ./app /app
# Note that there is no CMD to run because the CMD set in the base image is what we already wanted. As long as the Flask app is called `app`, the python file is named `main.py`, the parent directory is named `app`, and that same directory gets copied into `/app`, then the base image is designed to make our app work out-of-the-box.
我用来启动它的命令是:
docker build -t mvlancellotti/tennis-backend:prod -f prod.Dockerfile . && docker run --rm -p 5000:80 --name tennis-backend-container mvlancellotti/tennis-backend:prod
进入容器的/app
目录,有文件uwsgi.ini
,内容为:
[uwsgi]
module = main
callable = app
这似乎有效,文件/etc/nginx/nginx.conf
有内容:
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events
worker_connections 1024;
http
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
daemon off;
文件/etc/nginx/conf.d/nginx.conf
有内容:
server
listen 80;
location /
try_files $uri @app;
location @app
include uwsgi_params;
uwsgi_pass unix:///tmp/uwsgi.sock;
location /static
alias /app/static;
【问题讨论】:
你的 nginx/apache 配置是什么样的? @CodeLikeBeaker 请参阅我现在在上面添加的nginx.conf
。
@CodeLikeBeaker 我现在在上面添加了第二个nginx.conf
文件。我不知道为什么有两个。
【参考方案1】:
问题是,从请求资源的服务器应该有 CORS 标头(让它是 serverA
),而不是需要远程的服务器文件 (serverB
)。如果serverA
是NGINX
或Apache2
,您可以使用它在响应中添加CORS 标头:
NGINX:
add_header Access-Control-Allow-Origin *;
# or
add_header Access-Control-Allow-Origin serverB;
Apache2:
Header set Access-Control-Allow-Origin "*"
# or
Header set Access-Control-Allow-Origin "serverB"
上面的意思是“允许*或serverB从这个服务器(serverA)获取资源”。
对于刚接触 CORS 的任何人,我推荐这两个(1、2)来自 Mozilla 的简短读物。我认为他们很擅长解释基础知识。
【讨论】:
我已将add_header Access-Control-Allow-Origin *;
添加到我的/etc/nginx/conf.d/nginx.conf
文件中,但同样的CORS 错误仍然存在:跨源请求被阻止:同源策略不允许读取localhost:5000/… 的远程资源. (原因:CORS 请求未成功)。
请查看我在上面提供的额外信息。
@mareoraft 你把那条线放在哪里了?从配置来看,您应该将其放在location @app
或location /
中。或者两者兼而有之,如果初始请求可能在两个地方结束。还记得在进行更改后重新启动或重新加载 NGINX。
我已将该行放入location /
块中,现在我已将其放入两者中,但错误仍然存在。我实际上是在修改文件之前使用exec /usr/bin/supervisord
启动服务器,这显然启动了Supervisor,“使用Nginx 和uWSGI”。
为什么我们确信 nginx 是这里唯一的问题?我们可以采取哪些日志记录/故障排除步骤来查看 nginx 是否正在接收并阻止请求?浏览器给出的错误消息是localhost
而不是162.243.168.182
,这非常可疑。让我看看前端是否使用了正确的 url...【参考方案2】:
请注意,您需要在生产环境中考虑几件事情。 首先,请分享您与 docker 相关的配置(如果您有 Dockerfile、Docker-Compose),因为您正在尝试在与根本原因无关的地方解决问题。例如,如果您使用 Apache 或 Nginx 将 HTTP 请求作为 reverse_proxy 提供服务,那么您需要将相关标头添加到它们的配置中。但我建议你使用下面的代码,以前我遇到过像你这样的问题,下面的代码解决了我的问题:
cors = CORS(flask_app, resources=r"/api/*": "origins": "*", "allow_headers": "*", "expose_headers": "*")
更新:添加 Nginx 配置
这是 Nginx 最完整和最广泛的 CORS 配置:
Nginx 配置完成 CORS 打开
对于某些配置,某些旧浏览器不支持通配符(* 符号),因此,我添加了一个包含所有可能值的列表,但您需要根据您的应用程序要求和策略对其进行修改。此外,您可以将“add_headers”部分移动到配置文件中需要的任何位置。
server
listen 80;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'CONNECT, DEBUG, DELETE, DONE, GET, HEAD, HTTP, HTTP/0.9, HTTP/1.0, HTTP/1.1, HTTP/2, OPTIONS, ORIGIN, ORIGINS, PATCH, POST, PUT, QUIC, REST, SESSION, SHOULD, SPDY, TRACE, TRACK';
add_header 'Access-Control-Allow-Headers' 'Accept, Accept-CH, Accept-Charset, Accept-Datetime, Accept-Encoding, Accept-Ext, Accept-Features, Accept-Language, Accept-Params, Accept-Ranges, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Request-Headers, Access-Control-Request-Method, Age, Allow, Alternates, Authentication-Info, Authorization, C-Ext, C-Man, C-Opt, C-PEP, C-PEP-Info, CONNECT, Cache-Control, Compliance, Connection, Content-Base, Content-Disposition, Content-Encoding, Content-ID, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Script-Type, Content-Security-Policy, Content-Style-Type, Content-Transfer-Encoding, Content-Type, Content-Version, Cookie, Cost, DAV, DELETE, DNT, DPR, Date, Default-Style, Delta-Base, Depth, Derived-From, Destination, Differential-ID, Digest, ETag, Expect, Expires, Ext, From, GET, GetProfile, HEAD, HTTP-date, Host, IM, If, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Keep-Alive, Label, Last-Event-ID, Last-Modified, Link, Location, Lock-Token, MIME-Version, Man, Max-Forwards, Media-Range, Message-ID, Meter, Negotiate, Non-Compliance, OPTION, OPTIONS, OWS, Opt, Optional, Ordering-Type, Origin, Overwrite, P3P, PEP, PICS-Label, POST, PUT, Pep-Info, Permanent, Position, Pragma, ProfileObject, Protocol, Protocol-Query, Protocol-Request, Proxy-Authenticate, Proxy-Authentication-Info, Proxy-Authorization, Proxy-Features, Proxy-Instruction, Public, RWS, Range, Referer, Refresh, Resolution-Hint, Resolver-Location, Retry-After, Safe, Sec-Websocket-Extensions, Sec-Websocket-Key, Sec-Websocket-Origin, Sec-Websocket-Protocol, Sec-Websocket-Version, Security-Scheme, Server, Set-Cookie, Set-Cookie2, SetProfile, SoapAction, Status, Status-URI, Strict-Transport-Security, SubOK, Subst, Surrogate-Capability, Surrogate-Control, TCN, TE, TRACE, Timeout, Title, Trailer, Transfer-Encoding, UA-Color, UA-Media, UA-Pixels, UA-Resolution, UA-Windowpixels, URI, Upgrade, User-Agent, Variant-Vary, Vary, Version, Via, Viewport-Width, WWW-Authenticate, Want-Digest, Warning, Width, X-Content-Duration, X-Content-Security-Policy, X-Content-Type-Options, X-CustomHeader, X-DNSPrefetch-Control, X-Forwarded-For, X-Forwarded-Port, X-Forwarded-Proto, X-Frame-Options, X-Modified, X-OTHER, X-PING, X-PINGOTHER, X-Powered-By, X-Requested-With';
add_header 'Access-Control-Expose-Headers' 'Accept, Accept-CH, Accept-Charset, Accept-Datetime, Accept-Encoding, Accept-Ext, Accept-Features, Accept-Language, Accept-Params, Accept-Ranges, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Request-Headers, Access-Control-Request-Method, Age, Allow, Alternates, Authentication-Info, Authorization, C-Ext, C-Man, C-Opt, C-PEP, C-PEP-Info, CONNECT, Cache-Control, Compliance, Connection, Content-Base, Content-Disposition, Content-Encoding, Content-ID, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Script-Type, Content-Security-Policy, Content-Style-Type, Content-Transfer-Encoding, Content-Type, Content-Version, Cookie, Cost, DAV, DELETE, DNT, DPR, Date, Default-Style, Delta-Base, Depth, Derived-From, Destination, Differential-ID, Digest, ETag, Expect, Expires, Ext, From, GET, GetProfile, HEAD, HTTP-date, Host, IM, If, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Keep-Alive, Label, Last-Event-ID, Last-Modified, Link, Location, Lock-Token, MIME-Version, Man, Max-Forwards, Media-Range, Message-ID, Meter, Negotiate, Non-Compliance, OPTION, OPTIONS, OWS, Opt, Optional, Ordering-Type, Origin, Overwrite, P3P, PEP, PICS-Label, POST, PUT, Pep-Info, Permanent, Position, Pragma, ProfileObject, Protocol, Protocol-Query, Protocol-Request, Proxy-Authenticate, Proxy-Authentication-Info, Proxy-Authorization, Proxy-Features, Proxy-Instruction, Public, RWS, Range, Referer, Refresh, Resolution-Hint, Resolver-Location, Retry-After, Safe, Sec-Websocket-Extensions, Sec-Websocket-Key, Sec-Websocket-Origin, Sec-Websocket-Protocol, Sec-Websocket-Version, Security-Scheme, Server, Set-Cookie, Set-Cookie2, SetProfile, SoapAction, Status, Status-URI, Strict-Transport-Security, SubOK, Subst, Surrogate-Capability, Surrogate-Control, TCN, TE, TRACE, Timeout, Title, Trailer, Transfer-Encoding, UA-Color, UA-Media, UA-Pixels, UA-Resolution, UA-Windowpixels, URI, Upgrade, User-Agent, Variant-Vary, Vary, Version, Via, Viewport-Width, WWW-Authenticate, Want-Digest, Warning, Width, X-Content-Duration, X-Content-Security-Policy, X-Content-Type-Options, X-CustomHeader, X-DNSPrefetch-Control, X-Forwarded-For, X-Forwarded-Port, X-Forwarded-Proto, X-Frame-Options, X-Modified, X-OTHER, X-PING, X-PINGOTHER, X-Powered-By, X-Requested-With';
location /
try_files $uri @app;
location @app
include uwsgi_params;
uwsgi_pass unix:///tmp/uwsgi.sock;
location /static
alias /app/static;
Nginx 更简单的 CORS 配置文件
此外,您可以使用以下代码,但可能会遇到一些 cors 错误,您可以通过与之前的示例进行比较来快速解决这些错误。大多数情况下,响应中有一些标题在列表中是allowed和exposed。
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, HEAD';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Origin,Content-Type,Accept,Authorization,Access-Control-Expose-Headers,Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Expose-Headers';
add_header 'Access-Control-Expose-Headers' 'Origin,Content-Type,Accept,Authorization,Access-Control-Expose-Headers,Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Expose-Headers'
您还可以查看此链接以了解有关 CORS 的更多详细信息。 Link
【讨论】:
我使用了您建议的行(将/api/
修改为适当的路径),但我仍然收到CORS错误。
请查看我添加到问题中的其他信息。
@mareoraft,使用 /api/v1/ 进行 API 的版本管理,如果您为某些移动应用程序提供 API,这将很有用。您可以在 /v2/ 上发布新的主要 API,并保持 /v1/ 上的旧 API 向后兼容。但重点是其他参数,例如在我的 python 代码中公开标头和 ..,请检查它们。
谢谢。我明天去看看。我的问题尚未解决,但我正在接近。
再次感谢,祝您有美好的一天。【参考方案3】:
我希望添加评论,但仍然没有足够的代表..
Nginx add_header 对错误代码不起作用。
此外,当您收到任何类型的错误(400、500、502 等)时,标头也会丢失。浏览器会显示 CORS,但没关系,其他地方出了问题。 因此浪费很多时间是很常见的......
我访问了您的应用(抱歉,如果未提及这样做)。它加载并且一些过滤器选项导致 502 并且浏览器会说:哦,CORS! 但看起来有什么东西正在消亡然后又回来了。
关于 add_header 和错误的信息:https://serverfault.com/questions/431274/nginx-services-fails-for-cross-domain-requests-if-the-service-returns-error
【讨论】:
从您提到的链接add_header 'Access-Control-Allow-Origin' '*' always;
克服了错误代码的问题。 PS:有一些我的代表:)以上是关于为啥 Flask-Cors 在生产中没有检测到我的跨域域?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我在尝试设置 PayPal 付款时在生产中不断收到 INVALID_RESOURCE_ID 错误?