当前位置:   article > 正文

10.FastAPI响应体_fastapi 统一响应格式

fastapi 统一响应格式

10.FastAPI响应体

FastAPI支持声明响应体模型进行响应数据的处理。 可以在任意路由操作中使用 response_model 参数来声明用于响应的模型:

  • @app.get()

  • @app.post()

  • @app.put()

  • @app.delete()

  • 等等。

response_model是装饰器方法(get,post 等)的一个参数,而不是路由操作函数的参数;其类型与 Pydantic 模型属性所声明的类型相同,因此它可以是一个 Pydantic 模型,但也可以是一个由 Pydantic 模型组成的 list,例如 List[Item]。

在FastAPI 中,使用 response_model 的作用:

  • 将输出数据转换为其声明的类型。

  • 校验数据。

  • 在 OpenAPI 的路径操作中为响应添加一个 JSON Schema。

  • 并在自动生成文档系统中使用。

  • 会将输出数据限制在该模型定义内。

10.1响应模型

代码示例:

from fastapi import FastAPI
from pydantic import BaseModel
​
app = FastAPI()
​
class UsrIn(BaseModel):
    name: str
    sex: str
    acc: str
    pwd: str
​
class UsrOut(BaseModel):
    name: str
    sex: str
    acc: str
​
​
@app.post(path='/add_a_usr', response_model=UsrOut)
async def add_a_usr(usr: UsrIn):
    return usr

在上面的代码中,输入模型为UsrIn,输出模型为UsrOut,通过输出模型来屏蔽pwd字段的输出。执行请求:

curl -H "Content-Type: application/json;" -X POST -d "{\"name\":\"zhaomm\",\"sex\":\"woman\",\"acc\":\"zhaomm\",\"pwd\":\"Zhao68641018\"}" http://127.0.0.1:8000/add_a_usr
{
    "name":"zhaomm",
    "sex":"woman",
    "acc":"zhaomm"
}

10.2响应模型默认值

在FastAPI中,响应模型也可以具有默认值。

代码示例:

from fastapi import FastAPI
from fastapi import Body
from typing import Optional
from typing import List
from pydantic import BaseModel
​
app = FastAPI()
​
class ProductOut(BaseModel):
    id: str = Body(...)
    name: str = Body(...)
    price: Optional[float] = Body(10.0)
    desc: Optional[str] = Body(None)
​
​
@app.get(path='/find_products', response_model=List[ProductOut])
async def find_products():
    products = [
        {'id': 'p1', 'name': '玫瑰花'},
        {'id': 'p2', 'name': '百合花', 'price': 12.8},
        {'id': 'p3', 'name': '月季花', 'price': 19.8, 'desc': '月季花'}
    ]
    return products

执行请求:

curl http://127.0.0.1:8000/find_products
[
    {
        "id":"p1",
        "name":"玫瑰花",
        "price":10.0,
        "desc":null
    },{
        "id":"p2",
        "name":"百合花",
        "price":12.8,
        "desc":null
    },{
        "id":"p3",
        "name":"月季花",
        "price":19.8,
        "desc":"月季花"
    }
]

从上面的代码执行结果可以看出:当在实际输出中没有定义对应的字段属性值时,使用响应模型的默认值输出。如果希望响应结果中不输出默认值,仅输出实际数据,则可以在 可以设置路由操作装饰器的 response_model_exclude_unset 参数为真,下面代码在路由中增加该参数,代码如下:

from fastapi import FastAPI
from fastapi import Body
from typing import Optional
from typing import List
from pydantic import BaseModel
​
app = FastAPI()
​
class ProductOut(BaseModel):
    id: str = Body(...)
    name: str = Body(...)
    price: Optional[float] = Body(10.0)
    desc: Optional[str] = Body(None)
​
​
@app.get(path='/find_products', response_model=List[ProductOut], response_model_exclude_unset=True)
async def find_products():
    products = [
        {'id': 'p1', 'name': '玫瑰花'},
        {'id': 'p2', 'name': '百合花', 'price': 12.8},
        {'id': 'p3', 'name': '月季花', 'price': 19.8, 'desc': '月季花'}
    ]
    return products

执行请求:

curl http://127.0.0.1:8000/find_products
[
    {
        "id":"p1",
        "name":"玫瑰花"
    },{
        "id":"p2",
        "name":"百合花",
        "price":12.8
    },{
        "id":"p3",
        "name":"月季花",
        "price":19.8,
        "desc":"月季花"
    }
]

在以上情况下,如果实际数据值与默认值相同,那么FastAPI仍然会输出,也就是说,当设置response_model_exclude_unset=True时,其输出是按照实际值输出的。下面将代码{'id': 'p2', 'name': '百合花', 'price': 12.8}中的price字段值修改为10.0,然后执行请求:

curl http://127.0.0.1:8000/find_products
[
    {
        "id":"p1",
        "name":"玫瑰花"
    },{
        "id":"p2",
        "name":"百合花",
        "price":10.0
    },{
        "id":"p3",
        "name":"月季花",
        "price":19.8,
        "desc":"月季花"
    }
]

从上面的执行结果可以看出:虽然实际数据值与默认值相同,但仍然会输出。如果此时,不希望输出与默认值相同的数据值,可以设置 response_model_exclude_defaults参数为True,代码片段如下:

class ProductOut(BaseModel):
    id: str = Body(...)
    name: str = Body(...)
    price: Optional[float] = Body(10.0)
    desc: Optional[str] = Body(None)
​
@app.get(path='/find_products', response_model=List[ProductOut], response_model_exclude_defaults=True)
async def find_products():
    products = [
        {'id': 'p1', 'name': '玫瑰花'},
        {'id': 'p2', 'name': '百合花', 'price': 10.0},
        {'id': 'p3', 'name': '月季花', 'price': 19.8, 'desc': '月季花'}
    ]
    return products

执行请求:

curl http://127.0.0.1:8000/find_products
[
    {
        "id":"p1",
        "name":"玫瑰花"
    },{
        "id":"p2",
        "name":"百合花"
    },{
        "id":"p3",
        "name":"月季花",
        "price":19.8,
        "desc":"月季花"
    }
]

在上面的执行结果中:{"id":"p2","name":"百合花"}没有输出price字段的实际数据值,因为与默认值相同。

另外还有一个装饰器参数: response_model_exclude_none,如果设置该参数为True,则表示在响应模型中不输出None值。代码片段如下:

@app.get(path='/find_products', response_model=List[ProductOut], response_model_exclude_none=True)
async def find_products():
    products = [
        {'id': 'p1', 'name': '玫瑰花'},
        {'id': 'p2', 'name': '百合花', 'price': 10.0},
        {'id': 'p3', 'name': '月季花', 'price': 19.8, 'desc': '月季花'}
    ]
    return products

执行请求:

curl http://127.0.0.1:8000/find_products
[
    {
        "id":"p1",
        "name":"玫瑰花",
        "price":10.0
    },{
        "id":"p2",
        "name":"百合花",
        "price":10.0
    },{
        "id":"p3",
        "name":"月季花",
        "price":19.8,
        "desc":"月季花"
    }
]

10.3显式声明响应模型的字段

可以使用装饰器参数response_model_include 和 response_model_exclude来显式指明包含或者排除的字段,这两个参数接受由字符串组成的set类型,也可以使用list或tuple,但使用list或tuple最终会转换为set类型。

在Python中,set类型使用一组花括号定义,如:{'sex', 'name'}。

代码示例:

@app.get(path='/find_products', response_model=List[ProductOut], response_model_include={'price', 'desc'})
async def find_products():
    products = [
        {'id': 'p1', 'name': '玫瑰花'},
        {'id': 'p2', 'name': '百合花', 'price': 10.0},
        {'id': 'p3', 'name': '月季花', 'price': 19.8, 'desc': '月季花'}
    ]
    return products

执行请求:

curl http://127.0.0.1:8000/find_products
[
    {
        "price":10.0,
        "desc":null
    },{
        "price":10.0,
        "desc":null
    },{
        "price":19.8,
        "desc":"月季花"
    }
]

10.4响应模型Union

在FastAPI中,响应模型可以设置为多个类型的Union,响应为多种类型中的一种;定义该类型使用标准的Python类型提示typing.Union,原则上应该是先使用详细的类型,然后使用粗略的类型。代码示例:

from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
​
app = FastAPI()
​
class BaseItem(BaseModel):
    description: str
    type: str
​
class CarItem(BaseItem):
    type = "car"
    length: int
​
class PlaneItem(BaseItem):
    type = "plane"
    size: int
​
items = {
    "item1": {"description": "All my friends drive a low rider", "type": "car", "length": 200},
    "item2": {
        "description": "Music is my aeroplane, it's my aeroplane",
        "type": "plane",
        "size": 5,
    },
}
​
​
@app.get("/items/{item_id}", response_model=Union[PlaneItem, CarItem])
async def read_item(item_id: str):
    return items[item_id]

执行请求:

curl http://127.0.0.1:8000/items/item1
{
    "description":"All my friends drive a low rider",
    "type":"car",
    "length":200
}
C:\Users\Administrator>curl http://127.0.0.1:8000/items/item2
{
    "description":"Music is my aeroplane, it's my aeroplane",
    "type":"plane",
    "size":5
}

10.5任意 dict 构成的响应

在FastAPI中,如果事先不确定返回值的键名称,但可以确定返回的数据类型,那么,可以使用只包含类型的dict作为响应模型,使用 typing.Dict。示例代码如下:

from fastapi import FastAPI
from typing import Dict
​
app = FastAPI()
​
@app.get(path='/test', response_model=Dict[str, int])
async def test():
    return {'foo': 20}

执行请求:

curl http://127.0.0.1:8000/test
{"foo":20}
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/416455
推荐阅读
相关标签
  

闽ICP备14008679号