为什么要参数校验
大家应该都听过一个笑话,一个程序员写好了咖啡厅程序。测试,点1杯咖啡,没问题。点0.1杯水,提示只能点整数杯,没问题。给客户用,客户点了个炒粉,咖啡厅炸了。
hhhh,这个笑话是想表达永远不要相信用户的输入。
那么我们在做Web服务,写程序的时候。对用户传来的url做参数校验是很有必要的。下面看看怎么做吧。
下面分为两个部分。
- 路径参数校验
- 查询参数校验
路径参数校验
使用Path为路径参数声明类型的校验和元数据(元素据指描述数据属性的信息, 比如数据的类型,取值范围)
import uvicorn
from typing import Annotated # 引入 Annotated
from fastapi import FastAPI, Path # 引入 Path
import asyncio
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")]
): # Annotated在参数列表中使用, 用于限制参数类型,声明元数据; Path限制数值范围
results = {"item_id": item_id}
return results
if __name__ == '__main__':
config = uvicorn.Config(app, host='127.0.0.1', port=8000)
server = uvicorn.Server(config)
asyncio.run(server.serve())
浏览器中输入 http://127.0.0.1:8000/items/5
会看到 {"item_id":5}
下面再看看Path能起到什么作用
这里Path里, ge=5, 指限制的范围是大于(greater than) 或 等于(equal) 5
import uvicorn
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get", ge=5)]
):
results = {"item_id": item_id}
return results
浏览器中输入 http://127.0.0.1:8000/items/6
会看到 {"item_id":6}
如果输入的值小于5, 会看到下面的内容
{
"detail": [
{
"type": "greater_than_equal",
"loc": [
"path",
"item_id"
],
"msg": "Input should be greater than or equal to 5",
"input": "3",
"ctx": {
"ge": 5
}
}
]
}
校验的类型也可以选择是float等
可选的声明数值校验:
gt:大于(greater tha n) ge:大于等于(greater than or equ al) lt:小于(less t han) le:小于等于(less than or equal)
查询参数校验
查询参数 q 的类型为 str,默认值为 None,因此它是可选的。
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
如果在浏览器中输入 http://127.0.0.1:8000/items/?q=test
会看到 {"items":[{"item_id":"Foo"},{"item_id":"Bar"}],"q":"test"}
如果在浏览器中输入 http://127.0.0.1:8000/items/
会看到 {"items":[{"item_id":"Foo"},{"item_id":"Bar"}]}
更多校验条件
- max_length
- min_length
- 正则表达式
max_length
首先从 fastapi 导入 Query:
现在,将 Query 用作查询参数的默认值,并将它的 max_length 参数设置为 5
import uvicorn
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=5)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
如果在浏览器中输入 http://127.0.0.1:8000/items/?q=testdata
会看到
{
"detail": [
{
"type": "string_too_long",
"loc": [
"query",
"q"
],
"msg": "String should have at most 5 characters",
"input": "testdata",
"ctx": {
"max_length": 5
}
}
]
}
min_length
添加 min_length 参数,限制查询参数的最小长度
import uvicorn
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, min_length=3, max_length=5)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
正则表达式
添加正则表达式,限制参数值必须匹配正则表达式
import uvicorn
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, min_length=3
, max_length=5, pattern="^test.?$")):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
正则表达式 "^test.?$" ,
^test 指以test开头, ^就是以什么开头。
.?指0个或1个任意字符
$ 指没有更多
因此 test,testa,testb都是匹配的, testabc是不匹配的
声明为必须参数
前面不是必须参数是因为使用 “str | None = Query()” 声明, 直接使用 “str = Query()” 声明, 那么这就是个必须参数
import uvicorn
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(min_length=3, max_length=5, pattern="^test.?$")):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
另一种方法, 将Query的默认值设置为 ...
import uvicorn
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(default=..., min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
声明更多元素据
使用title和description 添加对数据的描述
Query(default=None, title="Query string", description="Query string for the items to search in the database", min_length=3)