FastAPI http請求參數的接收
我最開始接觸FastAPI的時候,最搞不懂的就是POST方式是如何接收參數的。
GET請求參數
GET方式的參數有兩種,一種是路徑參數,一種是查詢參數。舉個例子來說明兩者的區別
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_root(item_id: int):
return {"item_id": item_id}
這種是直接寫在請求路徑里面的item_id
,就是路徑參數,(但是個人不喜歡用)
請求示例url, 比如本地8000端口啟動 http://127.0.0.1:8000/items/10
啟動方式,如文件名為main.py
(不限制但是必須和啟動的文件名對應) 都是測試環境啟動
安裝pip install uvicorn
才能使用
# 在最下面加上 這一句
if __name__ == "__main__":
import uvicorn
uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
或者用命令行啟動 注意:得和main.py在同一文件目錄下
uvicorn main:app --host=127.0.0.1 --port=8000 --reload
from fastapi import FastAPI
app = FastAPI()
@app.get("/bar")
async def read_item(name: str, age: int=18): # tip: 和python默認參數一樣,有默認參數寫在后面
return {"name": name, "age": age}
這種name
和age
就是查詢參數方式GET請求,也就是最常見的?
和&
符號請求方式了
如上請求示例url: http://127.0.0.1:8000/bar?age=22&name=foo
路徑參數和查詢參數,參數驗證的區別
路徑參數使用 Path
查詢參數使用Query
, 可以查看Path和Query的源碼,其實現方式和使用參數方式都差不多,我理解的就是用來驗證不用的請求方式的參數。參數有很多驗證規則。
from typing import Optional
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/bar/{foo}")
async def read_item(
foo: int = Path(1, title='描述'),
age: int = Query(..., le=120, title="年齡"),
name: Optional[str] = Query(None, min_length=3, max_length=50, regex="^xiao\d+$")
):
return {"foo": foo, "age": age, "name": name}
上述GET請求合法的請求url是
http://127.0.0.1:8000/bar/123?age=18 # Query(...)表示沒有默認參數必須得傳
# or或者
http://127.0.0.1:8000/bar/123?age=18&name=xiao1 # name可以不傳,傳了就要符合正則的限制xiao開頭數字結尾
以上驗證參數簡單描述
le 驗證數字age最大不超過120
min_length 驗證name最短3個字符
max_length 驗證name最長50個字符
regex 這個就是正則驗證 必須是xiao開頭數字結尾(當然前提你得熟悉正則)
還有就是Query(..., le=120)
... 表示沒有默認參數,必須要傳。
def Query( # noqa: N802 關於FastAPI源碼里面noqa我搜了下,是No Quality Assurance 見issues https://github.com/PyCQA/pycodestyle/issues/476 還是還是Guido提問的
default: Any,
*,
alias: str = None, # 請求參數別名 如上name字段 Query(..., alias="N-A-N-E") 個人一般認為headers里面得參數用的多比如headers里面自定義的 X-Token
# 接收name的url就得是這樣 http://127.0.0.1:8060/bar/123?age=18&N-A-N-E=xiaoming
title: str = None,
description: str = None,
gt: float = None,
ge: float = None,
lt: float = None,
le: float = None,
min_length: int = None,
max_length: int = None,
regex: str = None,
deprecated: bool = None, # True表示將要是廢棄的接口
**extra: Any,
)
更多驗證方式直接查看Query
或者Path
源碼,看到參數名字,如上Query源碼,簡單的英語就知道是干什么用的。如果還不清楚查看官網
異常處理
額,我感覺我博客順序有點問題,應該先講參數處理再說異常捕獲的,詳細異常捕獲可以查看前一個博客。
比如上面的 http://127.0.0.1:8000/bar/123
這個就是參數驗證失敗了,會觸發RequestValidationError
異常
{
"detail": [
{
"loc": [
"query",
"age"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
捕獲參數異常
from typing import Optional
from fastapi import FastAPI, Request, Path, Query
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def request_validation_exception_handler(request: Request, exc: RequestValidationError):
print(f"參數不對{request.method} {request.url}") # 可以用日志記錄請求信息,方便排錯
return JSONResponse({"code": "400", "message": exc.errors()})
@app.get("/bar/{foo}")
async def read_item(
foo: int = Path(1, title='描述'),
age: int = Query(..., le=120, title="年齡"),
name: Optional[str] = Query(None, min_length=3, max_length=50, regex="^xiao\d+$")
):
return {"foo": foo, "age": age, "name": name}
這樣的話,就可以捕獲,然后按照自己定義的方式響應json格式。
還是上面的http://127.0.0.1:8000/bar/123
就會如下返回
{
"code": "400",
"message": [
{
"loc": [
"query",
"age"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
如果還不熟悉Python3.6之后的typing
模塊就要好好補補了:typing模塊官網傳送門 https://docs.python.org/zh-cn/3/library/typing.html
POST 請求參數
還是上面那個例子改成POST請求,與Query
或者Path
不一樣的就是,使用Body
函數來限制參數格式, 如下: Body和Query,Path用法基本是一樣的。
embed=True
意思是請求體中,使用json key-value形式, 參考官網
from fastapi import FastAPI, Request, Body
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def request_validation_exception_handler(request: Request, exc: RequestValidationError):
print(f"參數不對{request.method} {request.url}")
return JSONResponse({"code": "400", "message": exc.errors()})
@app.post("/bar")
async def read_item(
foo: int = Body(1, title='描述', embed=True),
age: int = Body(..., le=120, title="年齡", embed=True),
name: str = Body(..., regex="^xiao\d+$", embed=True)
):
return {"foo": foo, "age": age, "name": name}
正確的請求方式是 注意header請求頭里面"accept: application/json"
curl -X POST "http://127.0.0.1:8000/bar" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"foo\":1,\"age\":2,\"name\":\"333\"}"
Python requests發送請求
import requests
res = requests.post("http://127.0.0.1:8000/bar", json={"foo": 1, "age": 12, "name": "xiao123"})
print(res.json()) # {'foo': 1, 'age': 12, 'name': 'xiao123'}
上述demo只能接收application/json
json方式的參數,表單POST
請求的方式是接收不到參數的,如form-data
只能使用Form
接收,下面是示例:
注意必須安裝 pip install python-multipart
才能接收Form參數官網傳送門
from fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login/")
async def login(username: str = Form(...), password: str = Form(...)):
return {"username": username}
POST參數驗證
POST方式除了使用Body
方法來驗證外,更多的時候是使用的pydantic
這個庫來驗證的
比如以上的POST
請求參數就可以換成一下例子
import re
from typing import Optional
from fastapi import FastAPI, Request, Body
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel, validator, conint, constr
app = FastAPI()
class UserInfo(BaseModel):
foo: int = 1
age: conint(le=120) = 18
# 這里 regex驗證 我不知道為什么PyCharm提示語法錯誤
name: constr(min_length=5, max_length=10, regex=r"^xiao\d+$")
# 復雜的驗證一般用這個
@validator('name')
def name_re(cls, v):
# 自定義驗證 正則驗證name字段 等同於上面的正則驗證
if not re.match(r"^xiao\d+$", v):
# 拋出 ValueError pydantic接收到后會向外拋出 ValidationError
raise ValueError("name格式驗證錯誤")
return v
# FastAPI RequestValidationError本就是繼承的ValidationError, 會捕獲到請求參數異常錯誤
@app.exception_handler(RequestValidationError)
async def request_validation_exception_handler(request: Request, exc: RequestValidationError):
print(f"參數不對{request.method} {request.url}")
return JSONResponse({"code": "400", "message": exc.errors()})
@app.post("/bar")
async def read_item(
user_info: UserInfo
):
return {"foo": user_info.foo, "age": user_info.age, "name": user_info.name}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
上述例子正確訪問方式應是
curl -X POST "http://127.0.0.1:8000/bar" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"foo\":1,\"age\":2,\"name\":\"xiao1\"}"
關於POST form表單參數接收
記得一定要安裝 這個庫,否則接收不到值, 其他的用法都差不多。
pip install python-multipart
參考官網 https://fastapi.tiangolo.com/tutorial/request-forms/ 這里不在贅述,用法差不多
總結
Path
方法獲取請求路徑里面的參數如 http://127.0.0.1:8000/bar/123Query
方法獲取請求路徑后面的查詢參數如 http://127.0.0.1:8000/bar?name=xiaoming&age=18Body
方法獲取請求體里面的參數,前提是請求頭得用accept: application/json
當然我POST
例子中只列舉了獲取Body參數的例子,也可以獲取路徑參數Path
和查詢參數Query
完整代碼GitHub地址
更多FastAPI信息可以關注人網站
見個人網站 https://www.charmcode.cn/article/2020-07-23_fastapi_get_post