【FastAPI 學習 七】GET和POST請求參數接收以及驗證


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}

這種nameage就是查詢參數方式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/ 這里不在贅述,用法差不多

總結

當然我POST例子中只列舉了獲取Body參數的例子,也可以獲取路徑參數Path和查詢參數Query

完整代碼GitHub地址

更多FastAPI信息可以關注人網站

見個人網站 https://www.charmcode.cn/article/2020-07-23_fastapi_get_post


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM