参考内容 :
在fastapi教程的前几篇教程里,我们学习了如何声明路径参数、查询参数和请求体,这篇我们会对这些参数进行扩展学习,学习更多的用法
Query Parameters 我们先看一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 from typing import Optional from fastapi import FastAPI, Queryapp = FastAPI() @app.get("/items/" ) async def read_items (q: Optional [str ] = Query(None , min_length=3 , max_length=50 , regex="^fixedquery$" ) ): results = {"items" : [{"item_id" : "Foo" }, {"item_id" : "Bar" }]} if q: results.update({"q" : q}) return results
与之前的例子相比,发生了一些改变:
1 from fastapi import FastAPI, Query
1 async def read_items (q: Optional [str ] = Query(None , min_length=3 , max_length=50 , regex="^fixedquery$" ) ):
声明参数的时候,不再是单纯的指定None
,而是调用了Query
。 通过这种方式,对q
做了验证限制:
非必填
最大长度为50
最小长度为3
可以匹配正则表达式
尝试请求http://127.0.0.1:8000/items/?q=a
,会发现返回了错误提示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { "detail" : [ { "loc" : [ "query" , "q" ], "msg" : "ensure this value has at least 3 characters" , "type" : "value_error.any_str.min_length" , "ctx" : { "limit_value" : 3 } } ] }
再次尝试http://127.0.0.1:8000/items/?q=test
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { "detail" : [ { "loc" : [ "query" , "q" ], "msg" : "string does not match regex \"^fixedquery$\"" , "type" : "value_error.str.regex" , "ctx" : { "pattern" : "^fixedquery$" } } ] }
除此之外,Query
还支持其他的属性:
alias: 参数别名
description: 参数描述
title:参数标题
deprecated:表示该接口已被移除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from typing import Optional from fastapi import FastAPI, Queryapp = FastAPI() @app.get("/items/" ) async def read_items ( q: Optional [str ] = Query(..., title="Query string" , description="Query string for the items to search in the database that have a good match" , alias="item-query" ) ): results = {"items" : [{"item_id" : "Foo" }, {"item_id" : "Bar" }]} if q: results.update({"q" : q}) return results
打开http://127.0.0.1:8000/docs
, 我们会发现页面发生了变化,并且请求的参数名也变成了别名: 这里我们会发现item-query
变成了必填,这是因为,我们将Query(None)
替换成了Query(...)
Path Parameters 依旧是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from fastapi import FastAPI, Path, Queryapp = FastAPI() @app.get("/items/{item_id}" ) async def read_items ( *, item_id: int = Path(..., title="The ID of the item to get" , ge=0 , le=1000 ), q: str , size: float = Query(..., gt=0 , lt=10.5 ) ): results = {"item_id" : item_id} if q: results.update({"q" : q}) return results
在这里,用Path
来声明路径参数,通过这种方式,对item_id
做了验证限制:
item_id
是必填的
item_id
大于等于0
item_id
小于等于1000
除此之外,我们可以发现方法的第一个参数是*
,它的作用什么呢? 如果我们想不用Query
来声明q
,并想将q设为必填的,并且想用Path
来声明item_id
,这时我们就要用到*
。因为python并不会对*
做任何操作,但是python会将*
之后的参数作为关键字参数来看待,因此,即使没有用Query
,python也会把它当作有Query(...)
来待
在从fastapi
导入Path
、Query
时,其实导入的是方法,当它们被调用的时候,会返回同名的类。这些同名的类都是Param
的子类,因此在声明参数时,他们都是有相同的属性的。
Body Parameters 多个body参数 还是以一个例子开始
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from typing import Optional from fastapi import FastAPIfrom pydantic import BaseModelapp = FastAPI() class Item (BaseModel ): name: str description: Optional [str ] = None price: float tax: Optional [float ] = None class User (BaseModel ): username: str full_name: Optional [str ] = None @app.put("/items/{item_id}" ) async def update_item (item_id: int , item: Item, user: User ): results = {"item_id" : item_id, "item" : item, "user" : user} return results
在这个例子里,fastapi会注意到不止一个body参数,此时的请求体是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 { "item" : { "name" : "string" , "description" : "string" , "price" : 0 , "tax" : 0 }, "user" : { "username" : "string" , "full_name" : "string" } }
fastapi会根据请求自动处理参数,以便用item和user可以接收到其内容 假如此时我们想在请求体中增加一个参数,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 { "item" : { "name" : "Foo" , "description" : "The pretender" , "price" : 42.0 , "tax" : 3.2 }, "user" : { "username" : "dave" , "full_name" : "Dave Grohl" }, "importance" : 5 }
这时我们该如何写参数呢
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from typing import Optional from fastapi import Body, FastAPIfrom pydantic import BaseModelapp = FastAPI() class Item (BaseModel ): name: str description: Optional [str ] = None price: float tax: Optional [float ] = None class User (BaseModel ): username: str full_name: Optional [str ] = None @app.put("/items/{item_id}" ) async def update_item ( item_id: int , item: Item, user: User, importance: int = Body(... ) ): results = {"item_id" : item_id, "item" : item, "user" : user, "importance" : importance} return results
嵌入单一body参数 一般情况下,我们声明一个body参数时,fastapi会这样解析:
1 2 3 4 5 6 { "name" : "string" , "description" : "string" , "price" : 0 , "tax" : 0 }
如果我们想要下面这样的结构呢
1 2 3 4 5 6 7 8 { "item" : { "name" : "string" , "description" : "string" , "price" : 0 , "tax" : 0 } }
可以这样写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from typing import Optional from fastapi import Body, FastAPIfrom pydantic import BaseModelapp = FastAPI() class Item (BaseModel ): name: str description: Optional [str ] = None price: float tax: Optional [float ] = None @app.put("/items/{item_id}" ) async def update_item (item_id: int , item: Item = Body(..., embed=True ) ): results = {"item_id" : item_id, "item" : item} return results
Field 前面我们学习了如果给Query
、Path
增加验证限制,这里会介绍Body
如何增加验证限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from typing import Optional from fastapi import Body, FastAPIfrom pydantic import BaseModel, Fieldapp = FastAPI() class Item (BaseModel ): name: str description: Optional [str ] = Field( None , title="The description of the item" , max_length=300 ) price: float = Field(..., gt=0 , description="The price must be greater than zero" ) tax: Optional [float ] = None @app.put("/items/{item_id}" ) async def update_item (item_id: int , item: Item = Body(..., embed=True ) ): results = {"item_id" : item_id, "item" : item} return results
通过上面的例子不难看出Field
的属性和Query
、Path
是一样的。上面我们说过Query
、Path
是Param
的子类,这里的Field
本身是方法,返回了FieldInfo
类,而Param
是FieldInfo
的子类,因此他们的属性都是一样的Body
也是一个方法,他返回了继承自FieldInfo
的Body
类
上述栗子均放到git上啦,地址:戳这里