FastAPI--参数提交Request Body(3)

发布时间:2020-06-22 13:50:48编辑:admin阅读(3041)

    一、概述

    一般对于Request Body不会通过get提交,对于get提交的参数一般称为是查询参数。所以,如果是通过POTS,PUT等方式提交的参数信息,我们一般是放到Request Body来提交到我们的后端。

    对于如何接收和校验请求体,FastApi提供的形式是使用:from pydantic import BaseModel

    示例如下:

    import uvicorn
    from fastapi import FastAPI
    from pydantic import BaseModel
    
    class Item(BaseModel):
        name: str
        description: str = None
        price: float
        tax: float = None
    
    app = FastAPI()
    
    @app.post("/items/")
    async def create_item(item: Item):
        return item
    
    if __name__ == '__main__':
        uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)

    在上面的模型中,如果提交的Item它必须是怎么样的一个格式,比如name是必选字段,description是可选且默认为None, price是必选,且需要是float类型的,tax是可须且默认为None。

    那客户端如何提交上面那些参数呐?

    尝试提交参数什么都不写的情况下:

    http://127.0.0.1:8000/items/

    1.png

    使用JSON格式提交参数的情况下:

    {
        "name":"Foo",
        "description":"An openfdsf",
        "price":45.4,
        "tax":3.5
    }

     

    1.png

    故意提交错误参数格式请求:

    {
        "name":"Foo",
        "description":"An openfdsf",
        "price":"45abc",
        "tax":3.5
    }

     

    1.png

    Request Body 和 Query 和 Path的混合

    在设计一些API过程中难免的可能也会需要综合遇到上述的一些混搭的组合,需要同时多个参数的提交和获取

    那么我们通常接收这次参数的话一般怎么接收呐?

    示例代码如:

    import uvicorn
    from fastapi import FastAPI, Path
    from pydantic import BaseModel
    
    app = FastAPI()
    
    class Item(BaseModel):
        name: str
        description: str = None
        price: float
        tax: float = None
    
    @app.put("/items/{item_id}")
    async def update_item(
            *,
            item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000),
            q: str = None,
            item: Item = None,
    ):
        results = {"item_id": item_id}
        if q:
            results.update({"q": q})
        if item:
            results.update({"item": item})
        return results
    
    if __name__ == '__main__':
        uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)

    通过之前的学习,其实也很简单道理也还是一样,如上的示例请求的话:

     

    url:

    http://127.0.0.1:8000/items/1000?q=xiao

    参数:

    {
        "name":"Foo",
        "description":"An openfdsf",
        "price": 45.4,
        "tax":3.5
    }

    效果如下:

    1.png

    多个Request Body的提交

    更复杂的业务其实会存在多体的Boay的提交,之前做的商城下单里面,客户端有可能就会同时提交多个实体的对象信息到后端,如订单实体,地址实体,商品信息实体等。

    那么在Fastapi如何接受多个Body实体呐?通常以前的话,在bottle,通常直接的request.body 或 request.json就可以获取客户端部提交的信息了。

    在Fastapi假设客户端提交的参数是这样的形式:

    {
        "item": {
            "name": "Foo",
            "description": "The pretender",
            "price": 42.0,
            "tax": 3.2
        },
        "user": {
            "username": "dave",
            "full_name": "Dave Grohl"
        }
    }

     

    那如何的接收处理呐?

    import uvicorn
    from fastapi import FastAPI, Path
    from pydantic import BaseModel
    
    app = FastAPI()
    
    class Item(BaseModel):
        name: str
        description: str = None
        price: float
        tax: float = None
    
    class User(BaseModel):
        username: str
        full_name: 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
    
    if __name__ == '__main__':
        uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)

    这种情况,其实就是客户端提交多个实体对象。那可以定义多个模型对象即可。fastapi它会自动帮你处理提取信息。

    http://127.0.0.1:8000/items/1000

     

    1.png

     

    如果另外再假设:

    在Fastapi假设客户端提交的参数是这样的形式:

    {
        "item": {
            "name": "Foo",
            "description": "The pretender",
            "price": 42.0,
            "tax": 3.2
        },
        "user": {
            "username": "dave",
            "full_name": "Dave Grohl"
        },
        "importance": 5
    }

    其实这种可能也不是不存在滴,那如何的读取解析importance参数呐?既然参数有Query 和 Path,当然也会有 Body 。

    import uvicorn
    from fastapi import Body, FastAPI
    from pydantic import BaseModel
    
    app = FastAPI()
    
    class Item(BaseModel):
        name: str
        description: str = None
        price: float
        tax: float = None
    
    class User(BaseModel):
        username: str
        full_name: str = None
    
    @app.put("/items/{item_id}")
    async def update_item(
            *, item_id: int, item: Item, user: User, importance: int = Body(..., gt=0)
    ):
        results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
        return results
    
    if __name__ == '__main__':
        uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)

    上面的代码中我们引入了Body 并且在importance: int = Body(...)进行处理和提取:

    1.png

     

    如果另外再假设,客户端提交的是一个单体对象内嵌的话,我们需要怎么处理?:

    {
        "item": {
            "name": "Foo",
            "description": "The pretender",
            "price": 42.0,
            "tax": 3.2
        }
    }

    FastAPI提供了一个:

    item: Item = Body(..., embed=True) 具体如下:
    import uvicorn
    from fastapi import Body, FastAPI
    from pydantic import BaseModel
    
    app = FastAPI()
    
    
    class Item(BaseModel):
        name: str
        description: str = None
        price: float
        tax: 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
    
    if __name__ == '__main__':
        uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)

     

    请求示例如:

    1.png

     

     

    如果另外再假设,客户端提交一个更复杂的嵌套模型的话,怎么办?麻蛋的 肯定也是会有这样的情况滴! 嵌套里面有列表有实体。比如:

    {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2,
        "tags": ["rock", "metal", "bar"],
        "image": {
            "url": "http://example.com/baz.jpg",
            "name": "The Foo live"
        }
    }

    这时候,我们就需要所谓的子内嵌啦:

    import uvicorn
    from typing import Set
    
    from fastapi import FastAPI
    from pydantic import BaseModel
    
    app = FastAPI()
    
    class Image(BaseModel):
        url: str
        name: str
    
    class Item(BaseModel):
        name: str
        description: str = None
        price: float
        tax: float = None
        tags: Set[str] = []
        image: Image = None
    
    @app.put("/items/{item_id}")
    async def update_item(*, item_id: int, item: Item):
        results = {"item_id": item_id, "item": item}
        return results
    
    if __name__ == '__main__':
        uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)

    如上代码,Item里面包含了Image,也包含了,tags类型的列表定义。

    1.png

     

     

    MMP更深层的嵌套也是可以定义的如:

    {
        "name":"Foo",
        "description":"The pretender",
        "price":42,
        "items":[
            {
                "name":"Foo",
                "description":"The pretender",
                "price":42,
                "tax":3.2,
                "tags":[
                    "rock",
                    "metal",
                    "bar"
                ],
                "image":{
                    "url":"http://example.com/baz.jpg",
                    "name":"The Foo live"
                }
            },
            {
                "name":"Foo2",
                "description":"The 2",
                "price":422,
                "tax":3.2,
                "tags":[
                    "rock",
                    "metal",
                    "bar"
                ],
                "image":{
                    "url":"http://example.com/baz.jpg",
                    "name":"The Foo live"
                }
            }
        ]
    }

    对应的解析为:

    import uvicorn
    
    from fastapi import FastAPI
    from pydantic import BaseModel
    from typing import List, Set
    
    app = FastAPI()
    
    class Image(BaseModel):
        url: str
        name: str
    
    class Item(BaseModel):
        name: str
        description: str = None
        price: float
        tax: float = None
        tags: Set[str] = []
        # images: List[Image] = None
        image: Image = None
    
    class Offer(BaseModel):
        name: str
        description: str = None
        price: float
        items: List[Item]
    
    @app.post("/offers/")
    async def create_offer(*, offer: Offer):
        return offer
    
    if __name__ == '__main__':
        uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)

     

    请求url

    http://127.0.0.1:8000/offers

     

    1.png

    Request Body的Field

    Field字段的意思其实就是类似上面Query, Path,也同样给Body内的字段的信息添加相关的校验。

    也就是说。通过Field来规范提交的Body参数信息。比如:

    import uvicorn
    
    from fastapi import Body, FastAPI
    from pydantic import BaseModel, Field
    
    app = FastAPI()
    
    class Item(BaseModel):
        name: str
        description: str = Field(None, title="标题啊", description="错误提示文字啊", max_length=30)
        price: float = Field(..., gt=0, description="错误提示文字啊")
        tax: 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
    
    if __name__ == '__main__':
        uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)

     

    上面的意思就是和之前定义参数校验其实一样

    正常情况:

    {
        "item":{
            "name": "Foo",
            "description": "The pretender",
            "price": 42.0,
            "tax": 3.2
    
        }
    }

     

     1.png

     

     

    异常情况:

    {
        "item":{
            "name": "Foo",
            "description": "The pretender sssssssssssssssss",
            "price": 42.0,
            "tax": 3.2
    
        }
    }

    1.png

     

     

    其他数据类型的校验

    对于数据格式的校验,通常,我们不止于

    • int

    • float

    • str

    • bool

    但是提交参数不止于上述的几种格式,有时候比如是对手机号码的校验,有些时候是时间类型的校验等

    其他类型:

    其他数据类型¶ 以下是您可以使用的一些其他数据类型(来自官方文档):

    • UUID:

      • 一个标准的“通用唯一标识符”,在许多数据库和系统中常见于ID。

      • 在请求和答复中,将表示为str.

    • datetime.datetime:

      • 一只Pythondatetime.datetime.

      • 在请求和答复中,将表示为str采用ISO 8601格式,如:2008-09-15T15:53:00+05:00.

    • datetime.date:

      • Pythondatetime.date.

      • 在请求和答复中,将表示为str采用ISO 8601格式,如:2008-09-15.

    • datetime.time:

      • 一只Pythondatetime.time.

      • 在请求和答复中,将表示为str采用ISO 8601格式,如:14:23:55.003.

    • datetime.timedelta:

      • 一只Pythondatetime.timedelta.

      • 在请求和答复中,将表示为float总秒数。

      • Pydantic还允许将其表示为“ISO 8601时间差异编码”,有关更多信息,请参阅文档。.

    • frozenset:

      • 在请求和答复中,将其视为set:

      • 在请求中,将读取列表,消除重复,并将其转换为set.

      • 在答复中,set将转换为list.

      • 生成的架构将指定set值是唯一的(使用JSONSchema的uniqueItems).

    • bytes:

      • 标准Pythonbytes.

      • 在请求和答复中将被视为str.

      • 生成的架构将指定它是str带着binary“格式”。

    • Decimal:

      • 标准PythonDecimal.

      • 在请求和响应中,处理方式与float.

    所以我还可以使用其他类型来校验:

    import uvicorn
    
    from datetime import datetime, time, timedelta
    from uuid import UUID
    
    from fastapi import Body, FastAPI
    
    app = FastAPI()
    
    
    @app.put("/items/{item_id}")
    async def read_items(
            item_id: UUID,
            start_datetime: datetime = Body(None),
            end_datetime: datetime = Body(None),
            repeat_at: time = Body(None),
            process_after: timedelta = Body(None),
    ):
        start_process = start_datetime + process_after
        duration = end_datetime - start_process
        return {
            "item_id": item_id,
            "start_datetime": start_datetime,
            "end_datetime": end_datetime,
            "repeat_at": repeat_at,
            "process_after": process_after,
            "start_process": start_process,
            "duration": duration,
        }
    
    if __name__ == '__main__':
        uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)



    本文参考链接:

    http://www.zyiz.net/tech/detail-119883.html

     


关键字

上一篇: FastAPI--路由(2)

下一篇: FastAPI--响应报文(4)