一、概述
跨域資源共享(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
字段以后,確認是否允許跨源請求,就可以做出回應。
在這種情況下,中間件會攔截請求信息並且根據是否允許跨域請求返回不同的請求結果信息。