美文网首页FastAPI 解读 by Gascognya
FastAPI 官方文档解读 (一)

FastAPI 官方文档解读 (一)

作者: Gascognya | 来源:发表于2020-09-04 10:20 被阅读0次

    安装方式

    标准安装

    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值,不必遵循什么规则。yesonTruetrue1......皆可被转化。请您放心,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'}
    

    requestresponse中分别使用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: strp: 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)):
    

    便可以完成我们所期望的

    相关文章

      网友评论

        本文标题:FastAPI 官方文档解读 (一)

        本文链接:https://www.haomeiwen.com/subject/nqansktx.html