一、概述
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
FastAPI利用CORSMiddleware
中间件来实现CORS。
为啥需要跨域处理,通常我们的API一般是给到前端去调用,但是前端可能使用域名和没提供的API域名是不一样,这就引发了浏览器同源策略问题,所以我们需要做跨域请求支持。
FastAPI支持跨域的话,可以通过添加中间的形式,不仅如此他还支持仅限于支持哪些域名进行跨域请求:
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"http://api.zwnsyw.com",
"https://api.zwnsyw.com",
"http://localhost",
"http://localhost:8080",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
async def main():
return {"message": "Hello World"}
if __name__ == '__main__':
uvicorn.run(app='main:app', host="0.0.0.0", port=8000, reload=True, debug=True)
二、演示跨域
环境说明:
前端:
操作系统:centos 7.6
ip地址:192.168.31.35
运行软件:nginx
后端:
操作系统:windows 10
ip地址:192.168.31.61
运行软件:pycharm
请求api
登录到前端服务器,安装nginx,并启动。
yum install -y nginx nginx
访问默认页面
http://192.168.31.35/
测试页面
登录到前端服务器,默认的nginx页面目录为:/usr/share/nginx/html
新建一个测试文件
cd /usr/share/nginx/html vi test.html
内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<button id="api">请求接口</button>
<h4>结果</h4>
<div id="content"></div>
<script>
$('#api').click(function () {
$.ajax({ //发送ajax请求
url: 'http://192.168.31.61:8000/',
type: "get",
data: {},
success: function (arg) {
//arg = JSON.parse(arg);
console.log(arg);
$('#content').text(arg.message)
//return false;
},
error: function () {
console.log("网络请求错误!");
}
});
});
</script>
</body>
</html>
访问测试页面
http://192.168.31.35/test.html
点击请求接口按钮,提示跨域。
为什么会出现跨域呢?因为同源策略。
同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以192.168.31.35下的js脚本采用ajax读取192.168.31.61里面的文件数据是会被拒绝的。
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
三、解决跨域
一般解决跨域,是在后端完成的,设置允许跨域。
修改main.py,增加前端的url地址即可。
例如:前端服务器:http://43.226.150.147:39003 Fastapi服务器:http://43.226.150.147:8080 那么只需要将前端服务的地址或域名允许跨域就可以,也就是添加 http://43.226.150.147:39003 跨域
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
"""↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Fastapi ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"""
# 前端页面url
origins = [
"http://43.226.150.147:39003",
"http://www.zwnsyw.com",
"https://www.zwnsyw.com",
]
# 后台api允许跨域
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/api")
async def home():
return {"message": "Hello World"}
if __name__ == '__main__':
uvicorn.run(app='main:app', host="0.0.0.0", port=8000, reload=True, debug=True)
再次点击按钮,结果就会显示出来了。
四、使用CORSMiddleware
CORSMiddleware的参数默认值是受限制的,为了在跨域访问中支持相应的功能,我们应当显示指定具体参数的的信息。
CORSMiddleware
支持参数信息如下:
1、allow_origins
:允许跨域请求的域名列表,例如 ['https://example.org', 'https://www.example.org']
或者 ['*']
。
2、allow_origin_regex
:允许跨域请求的域名正则表达式,例如 'https://.*\.example\.org'
。
3、allow_methods
:允许跨域请求的HTTP方法列表,默认为['GET']
,['*']
表示允许所有HTTP方法。
4、allow_headers
:跨域请求支持的HTTP头信息列表。['*']
表示允许所有头信息。Accept
, Accept-Language
, Content-Language
和 Content-Type头信息默认全都支持。
5、allow_credentials
:表示在跨域请求时是否支持cookie,默认为False。
6、expose_headers
:表示对浏览器可见的返回结果头信息,默认为[]
。
7、max_age
:浏览器缓存CORS返回结果的最大时长,默认为600(单位秒)。
二、请求种类
浏览器将CORS请求分成两类:简单请求(Simple requests)和非简单请求,也叫预检请求(CORS preflight requests)。
只要同时满足以下两大条件,就属于简单请求。
(1) 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
(2)HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
凡是不同时满足上面两个条件,就属于非简单请求。
浏览器对这两种请求的处理,是不一样的。
1、简单请求
对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin
字段。
Origin
字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
在这种情况下,中间件会正常传递请求信息,但会在返回结果中包含恰当的CORS头信息。
2、预检请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json
。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP方法和头信息字段。只有得到肯定答复,浏览器才会发出正式的请求,否则就报错。
"预检"请求用的请求方法是OPTIONS
,表示这个请求是用来询问的。头信息里面,关键字段是Origin
,表示请求来自哪个源。
除了Origin
字段,"预检"请求的头信息包括两个特殊字段。
(1)Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT
。
(2)Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header
。
服务器收到"预检"请求以后,检查了Origin
、Access-Control-Request-Method
和Access-Control-Request-Headers
字段以后,确认是否允许跨源请求,就可以做出回应。