【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}
这种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