路由跨域
今天我们来聊一聊跨域问题,以及如何在 Flask 中解决它。当你在前后端开发中遇到跨域问题时,可以使用一些方法来绕过浏览器的安全限制,确保你的应用正常运行。
简介
在进行前后端联调的时候,可能会遇到下面这个报错:
Access to XMLHttpRequest at 'http://127.0.0.1:5000/login' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
你可能会遇到这样的错误提示。这个报错提示的其实就是“跨域限制”,它是发生在浏览器端的。让我们来简单了解一下跨域的问题。
这个错误就是跨域限制,是发生在浏览器端的。
浏览器中有一个同源策略,它是一个安全策略,用来限制源的交互。
- 同源策略:是一种约定,它是浏览器核心也最基本的安全功能,它会阻止一个域的 JS 脚本和另外一个域的内容进行交互,如果缺少了同源策略,浏览器很容易受到 XSS、CSFR 等攻击。所谓同源(即在同一个域)就是两个页面具有相同的协议(protocol)、主机(host)和端口号(port)。
- 跨域:当一个请求 URL 的协议、域名、端口三者之间的任意一个与当前页面 URL 不同即为跨域。
浏览器有一个叫“同源策略”的安全功能,旨在阻止不同源之间的互相访问。所谓“同源”是指,两个页面必须拥有相同的协议、主机和端口号,只有这些都一致时,才能进行互相操作。当页面想要请求的资源与当前页面的源不同,就会被认为是“跨域请求”。举个例子,假如前端页面是 http://localhost:8080,但是它请求的 API 地址是 http://127.0.0.1:5000,就会触发跨域问题。
场景示例
下面来看一个具体的跨域问题的场景。
创建 flask_cors_demo.py
# 后端服务代码
from flask import Flask
app = Flask(__name__)
@app.route('/hello')
def hello():
return "hello world"
if __name__ == '__main__':
app.run(debug=True, port=5055)
让我们通过一个实际的场景来看看跨域问题。这里有两个部分,前端代码和后端服务代码。首先是后端服务,简单的 Flask 应用。
templates 目录下创建 cors.html
<!-- 前端代码 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>跨域问题</title>
</head>
<body>
<div>
<button class="button" id="hello">click me</button>
</div>
</body>
</html>
<script type="text/javascript">
var btnHello = document.querySelector("button#hello");
btnHello.onclick = () => {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
xhr.withCredentials = true;
xhr.open("get", "http://localhost:5055/hello", true);
xhr.setRequestHeader("Content-Type", "text");
xhr.send();
};
</script>
接下来是前端页面,这个页面会触发跨域请求。
启动后端服务后,去访问前端页面,当点击按钮的时候,可以看到控制台报错:
当你启动后端服务后并访问前端页面,点击按钮时,你会在浏览器的控制台看到跨域错误。这个错误说明了浏览器因为“同源策略”而阻止了请求。
解决跨越问题
cors 是一个 w3c 的标准,全称为跨域资源共享,允许浏览器向跨域服务器发出请求。
- 安装 flask-cors:
pip install flask-cors
- 解决跨域问题分为两种
- 针对全局所有的 API
CORS(app)
- 针对特定的 API
@cross_origin()
- 针对全局所有的 API
域资源共享(CORS)是 W3C 提出的一个标准,允许浏览器向跨域的服务器发出请求。幸运的是,Flask 提供了一个简单的解决方案——使用 flask-cors 扩展。首先,你需要安装这个扩展,使用 pip install flask-cors 就可以。然后,我们可以通过两种方式来解决跨域问题。全局解决跨域问题:对所有的 API 开启跨域支持。局部解决跨域问题:只对特定的 API 开启跨域支持。
from flask import Flask
from flask_cors import CORS, cross_origin
app = Flask(__name__)
# 第一种:全局解决跨域问题
CORS(app, supports_credentials=True)
# 第二种:局部解决跨域问题
# @cross_origin(supports_credentials=True)
@app.route('/hello')
def hello():
return "hello world"
if __name__ == '__main__':
app.run(debug=True, port=5555)
你可以通过简单的 CORS(app) 来全局开启跨域支持。这样,所有的 API 请求都会自动支持跨域。如果你只想对特定的路由开启跨域支持,可以使用 @cross_origin() 装饰器。比如,只对 /hello 路由开放跨域支持。这样,只有 /hello 路由会支持跨域,其他路由不会受到影响。
跨域也可以结合蓝图去使用:
from flask import Flask, Blueprint
from flask_cors import cross_origin, CORS
app = Flask(__name__)
# 第一种:全局解决跨域问题
CORS(app, supports_credentials=True)
hello_route = Blueprint("hello", __name__, url_prefix="/hello")
# 第二种:局部解决跨域问题
# @cross_origin(supports_credentials=True)
# @app.route('/hello')
@hello_route.route("")
def hello():
return "hello world"
if __name__ == '__main__':
app.register_blueprint(hello_route)
app.run(debug=True, port=5055)
如果你在应用中使用了蓝图,跨域配置同样适用。你可以在全局应用 CORS(),或者在蓝图的路由上使用 @cross_origin() 装饰器。这样,即使使用了蓝图,你仍然可以灵活控制哪些路由支持跨域。
总结
- 跨域限制
- 解决跨域问题
今天我们讨论了跨域限制及其解决方案。跨域限制是浏览器出于安全考虑对不同源之间的请求进行的限制。通过 flask-cors 扩展,我们可以非常方便地解决跨域问题,无论是全局解决还是局部解决,都能确保前后端的正常交互。掌握了跨域问题的解决方案后,你就可以更轻松地进行前后端联调了。