安装方式
标准安装
pip install fastapi
&pip install uvicorn
完整安装
pip install fastapi[all]
启动方式
代码式启动(开发时)
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
命令式启动(任何时候)
uvicorn 模块名:app --port=8000 --host=127.0.0.1
API文档
Swagger UI
http://127.0.0.1:8000/docs
(常见情况)
ReDoc
http://127.0.0.1:8000/redoc
(常见情况)
OpenAPI
API的标准,由Swagger UI提出。API文档的基础便是OpenAPI
http://127.0.0.1:8000/openapi.json
(常见情况)
参数
路径参数
例如http://127.0.0.1:8000/items/foo
这种形式,在@app.get("/items/{item_id}")
环境下,foo
为{item_id}
的值,在下方可以def read_item(item_id: int):
这样接收。
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
枚举的用处
新建一个枚举类应用于参数类型
class ModelName(Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
@app.get("/model/{model_name}")
def get_model(model_name: ModelName):
API文档会很聪明的知道,参数必须限制在枚举类中的一个。但API还不清楚这个参数的类型。
class ModelName(str, Enum):
只需要让枚举类继承例如str即可,请您放心,FastAPI足够聪明。
如果您的路径参数,其值是一个路径
例如/files/home/johndoe/myfile.txt
,您的期望是/files/{file_path}
。也就是将home/johndoe/myfile.txt
解析为file_path
。
您需要改写为/files/{file_path:path}
,这样便可捕获到。
查询参数
常见的www.example.com/?name=abc&age=5
形式
def test(name: str, age: int):
如果需要接收bool值,不必遵循什么规则。yes
,on
,True
,true
,1
......皆可被转化。请您放心,FastAPI足够聪明。
默认值
就像正常Python那样标注
def test(name: str = `bob`, age: int):
具有默认值的参数,则是非必需的。
当然有时候=
后面未必代表的是默认值(这个以后谈起)
类型声明
正常像typing那样使用即可
Model
Model可用于body中的JSON序列,可以是request的body,也可以是response的body
class Item(BaseModel):
name: str
description: Optional[str] = None
@app.post("/items/")
def create_item(item: Item):
return {'msg': 'ok'}
@app.get("/items/", response_model=Item)
async def find_item(item_id: int):
return {'name': 'bob', 'description': 'good boy'}
在request和response中分别使用Model
参数验证
@app.get("/items/")
async def read_items(q: Optional[str] = Query(None, max_length=50)):
查询参数可使用Query()
进行验证,可设置许多属性,包括大于,小于,长度,默认值等。
q: str = Query(None)
&q: str = None
这两种情况是等价的。
当然如果你把第一个参数(default=)设置为...
是,这就变成了必填参数,即便你已经q = Query()
这种形式,但Query
这样就不能其默认值作用
def read_items(q: str = Query(..., min_length=3)):
查询参数列表
有趣的是,当你更改一下类型声明例如
def read_items(q: Optional[List[str]] = Query(None)):
,List[str] 会使得q可以存在多个
http://localhost:8000/items/?q=foo&q=bar
例如这样的形式便是被允许的。此时这个q会由str变为list
def read_items(q: List[str] = Query(["foo", "bar"])):
像这样你也可以为其添加默认值
def read_items(q: list = Query([]))
再例如这样的情况,注意,这个并未限制元素的类型。
添加更多细节
q: Optional[str] = Query(
None,
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
)
例如deprecated=True
可以将参数弃用,再例如alias
可以给参数起个别名。
路径参数同样也可以
使用Path()
便可以达到同样效果,注意,路径参数永远都是必须的。所以您最少也要应该写上...
作为default
参数顺序
q: str
和p: str = Path(...)
,这些写法都是可以的。但是如果您是强迫症,想定义他们的顺序。可能会碰到一些问题。python不允许带有默认值的参数,在不带有默认值的参数的前面。这会导致您不能像下面这种书写方式
def read_items(item_id: int = Path(...), q: str):
这是python语法上不允许的
为了解决这个问题,您可以在最前面加一个*
作为参数
def read_items(
*, item_id: int = Path(...), q: str
):
python不会对这个*
做什么,但是会将其后面所有的参数都解析成kwargs
形式。即便其没有写默认值。
def test(a, *, b=5, c, d=5):
print(a, b, c, d)
test(1, b=2, c=3, d=4)
请注意,这里*
前面的参数照旧,但*
后面的参数不允许用位置参数,而必须指定参数名。
Body
这里主要是讲JSON格式的Body,当我们需要一个model时,我们期待的body格式如下
class Item(BaseModel):
name: str
description: Optional[str] = None
@app.post("/items/")
def create_item(item: Item):
return {'msg': 'ok'}
{
"name": "apple",
"description": "ohhhhhhh!"
}
但是如果我们需要两个model,例如:
class Item(BaseModel):
name: str
description: Optional[str] = None
class User(BaseModel):
name: str
age: int
@app.post("/items/")
def create_item(item: Item, user: User):
return {'msg': 'ok'}
这种情况下,如果我们像如下这样是不可以的。因为这不符合JSON的格式。
{
"name": "apple",
"description": "ohhhhhhh!"
}
{
"name": "bob",
"age": 5
}
正确写法
{
"item": {
"name": "apple",
"description": "ohhhhhhh!"
},
"user": {
"name": "bob",
"age": 5
}
}
一定要注意 单model 和 多model 的区别
body内的单值
def create_item(item: Item, user: User, id: int):
这种情况下,这个id会被解析为查询参数。但如果您想让它也成为body体中的一部分。
{
"item": {
"name": "apple",
"description": "ohhhhhhh!"
},
"user": {
"name": "bob",
"age": 5
},
"id": 1
}
例如这是您所期待的,您希望id: int所解析的是body中的这个。那么您可以使用Body()
def create_item(item: Item, user: User, id: int, id: int=Body(...)):
这时,上面形式的JSON就会如您所愿解析了。
单个model也内嵌进JSON
def create_item(item: Item):
我们知道,该参数的期望body是
{
"name": "apple",
"description": "ohhhhhhh!"
}
但如果我们希望的格式是如下的,该怎么做?
{
"item": {
"name": "apple",
"description": "ohhhhhhh!"
}
}
我们知道,只有两个以上model时,才会自动变成key,value形式。
实际上我们只需要稍作加工
def create_item(item: Item=Body(..., embed=True)):
便可以完成我们所期望的
网友评论