• FastAPI请求系列(三) Request Body


    一、请求体和字段

    1、基础用法

    请求体的数据校验是使用Pydantic来进行声明,然后校验的。

    from typing import Optional
    from fastapi import FastAPI
    from pydantic import BaseModel
    
    
    class Item(BaseModel):
        name: str
        description: Optional[str] = None
        price: float
        tax: Optional[float] = None
    
    
    app = FastAPI()
    
    
    @app.post("/items/")
    async def create_item(item: Item):
        print(item.name)  # 获取请求体中的值
        return item

    请求体内容通过Item类提前定义好,内容包含4个字段,其中description和tax为可选字段,所以请求体内容为:

    {
        "name": "hello",
        "price": 30.2
    }

    也是可行的。值得注意的是声明参数时将其申明为定义好的Item类型。

    2、配置项

    如果调用方对于请求体不知道如何使用,可以通过额外的配置项进行说明:

    ...
    class Item(BaseModel):
        name: str
        description: Optional[str] = None
        price: float
        tax: Optional[float] = None
    
        class Config:
            schema_extra = {
                "example": {
                    "name": "apple",
                    "description": "this is a fruit ...",
                    "price": 3.14,
                    "tax": 1.2
                }
            }
    
    ...

    这样在API接口处会有对应的示例说明:

    3、字段

    与使用Query、Path的方式相同,Field在Pydantic模型内部声明校验和元数据。

    from typing import Optional
    from pydantic import BaseModel, Field
    
    
    class Item(BaseModel):
        name: str
        description: Optional[str] = Field(None, title="the description ...", max_length=20)
        price: float = Field(..., ge=2, description="price")
        tax: Optional[float] = None
    
        class Config:
            schema_extra = {
                "example": {
                    "name": "apple",
                    "description": "this is a fruit ...",
                    "price": 3.14,
                    "tax": 1.2
                }
            }
    ...

    注意,Field时从Pydantic中导入,这与Path与Query不同。

    Field可以传递校验的参数:

    def Field(
        default: Any = Undefined,
        *,
        default_factory: Optional[NoArgAnyCallable] = None,
        alias: str = None,
        title: str = None,
        description: str = None,
        const: bool = None,
        gt: float = None,
        ge: float = None,
        lt: float = None,
        le: float = None,
        multiple_of: float = None,
        min_items: int = None,
        max_items: int = None,
        min_length: int = None,
        max_length: int = None,
        allow_mutation: bool = True,
        regex: str = None,
        **extra: Any,
    ) -> Any:
    ...

    二、多参数混合

    一个请求中可能有路径参数、查询参数以及请求体,当它们混合在一起又是如何处理的呢?不过在说明这个问题之前,需要注意的请求体中可能有多个请求体参数以及单个请求体参数。

    (一)多个请求体参数

    from typing import Optional
    from fastapi import FastAPI
    from pydantic import BaseModel, Field
    from datetime import date
    
    
    class Item(BaseModel):
        name: str
        description: Optional[str] = Field(None, title="the description ...", max_length=20)
        price: float = Field(..., ge=2, description="price")
        tax: Optional[float] = None
    
        class Config:
            schema_extra = {
                "example": {
                    "name": "apple",
                    "description": "this is a fruit ...",
                    "price": 3.14,
                    "tax": 1.2
                }
            }
    
    
    class User(BaseModel):
        username: str
        full_name: Optional[str] = None
        birthday: date
    
    
    app = FastAPI()
    
    
    @app.post("/items/")
    async def create_item(item: Item, user: User):
        return {"item": item, "user": user}

    上面声明两两个请求体参数的模型,并且在返回过程中,键值分别时定义好的,所以被期望的返回值就是类似下面的:

    {
      "item": {
        "name": "apple",
        "description": "this is a fruit ...",
        "price": 3.14,
        "tax": 1.2
      },
      "user": {
        "username": "san",
        "full_name": "zhangsan",
        "birthday": "2021-06-03"
      }
    }

    注意birthday字段的类型时date类型,也就是除了常用的数据类型:

    • int
    • float
    • str
    • bool

    也可以定义其它复杂的数据类型:

    • uuid(str表示)
    • datetime.datetime (str表示,2021-06-03T15:53:00+05:00)
    • datetime.date(str表示,2021-06-03)
    • datetime.time(str表示,11:34:10.005)
    • datetime.timedelta(float表示)
    • bytes
    • Decimal(float表示)

    (二)单个请求体参数

    在正常情况下,如果只有一个请求体模型,被期望的返回值应该是这样的:

     {
        "name": "apple",
        "description": "this is a fruit ...",
        "price": 3.14,
        "tax": 1.2
      }

    但是如果希望达到和上面的多个请求体一样的效果,外面存在一个键,内部是请求体的json内容,就需要使用Body中的embled参数了。

    from typing import Optional
    from fastapi import FastAPI, Body
    from pydantic import BaseModel, Field
    
    
    class Item(BaseModel):
        name: str
        description: Optional[str] = Field(None, title="the description ...", max_length=20)
        price: float = Field(..., ge=2, description="price")
        tax: Optional[float] = None
    
        class Config:
            schema_extra = {
                "example": {
                    "name": "apple",
                    "description": "this is a fruit ...",
                    "price": 3.14,
                    "tax": 1.2
                }
            }
    
    
    
    app = FastAPI()
    
    
    @app.post("/items/")
    async def create_item(item: Item = Body(..., embed=True)):
        return {"item": item}

    其返回的结果就是:

    {
      "item": {
        "name": "apple",
        "description": "this is a fruit ...",
        "price": 3.14,
        "tax": 1.2
      }
    }

    (三)单值请求体参数

    如果请求中是一个单值或者是请求体的一部分,单值不会像上述请求体一样进行Pydantic模型设定,但是如果在函数的接受值中进行声明,显然FastAPI会将其当作一个查询参数,这时需要使用Body,这与Query和Path类似。

    from fastapi import FastAPI, Body
    
    app = FastAPI()
    
    @app.post("/single/value")
    async def single_value(importance: int = Body(...)):
        return {"importance": importance}

    这样,importance这个值会从请求体中传递给后台,而不是以查询参数的输入方式。

    (四)多参数混合

    from typing import Optional
    from fastapi import FastAPI, Body, Path
    from pydantic import BaseModel, Field
    from datetime import date
    
    
    class Item(BaseModel):
        name: str
        description: Optional[str] = Field(None, title="the description ...", max_length=20)
        price: float = Field(..., ge=2, description="price")
        tax: Optional[float] = None
    
        class Config:
            schema_extra = {
                "example": {
                    "name": "apple",
                    "description": "this is a fruit ...",
                    "price": 3.14,
                    "tax": 1.2
                }
            }
    
    
    class User(BaseModel):
        username: str
        full_name: Optional[str] = None
        birthday: date
    
    
    app = FastAPI()
    
    
    @app.post("/multi/params/{item_id}")
    async def multi_params(
            *,
            item_id: int = Path(..., title="item id ...", ge=1, le=10),
            q: Optional[str] = None,
            item: Optional[Item] = None,
            user: User,
            importance: int = Body(...)
    ):
        results = {"item_id": item_id, "user": user, "importance": importance}
        if q:
            results.update({"q": q})
        if item:
            results.update({"item": item})
        return results

    上述视图函数中包含路径参数、请求参数、多个请求体参数、单值请求体参数,接口中:

     注意,第一个参数"*",Python不会对"*"做任何事情,但是之后所有的参数都应作为关键字(键值对),也被称为kwargs来调用,即使它们没有默认值。

    三、嵌套模型

    from typing import Optional, List, Set
    from fastapi import FastAPI
    from pydantic import BaseModel, HttpUrl
    
    app = FastAPI()
    
    
    class Image(BaseModel):
        url: HttpUrl  # 对http进行验证
        name: str
    
    
    class Item(BaseModel):
        name: str
        description: Optional[str] = None
        price: float
        tax: Optional[float] = None
        tags: Set[str] = set()
        images: Optional[List[Image]] = None
    
    
    @app.put("/items/{item_id}")
    async def update_item(item_id: int, item: Item):
        return {"item_id": item_id, "item": item}

    上述的请求体被期待这样的请求格式:

    {
      "name": "item name",
      "description": "this is item...",
      "price": 0,
      "tax": 0,
      "tags": [],
      "images": [
        {
          "url": "http://127.0.0.1:8000/docs",
          "name": "zhangsan"
        },
        {
          "url": "http://127.0.0.1:8000/docs",
          "name": "lisi"
        }
      ]
    }

    上述模型中:

    • Image作为Item的一部分
    • 声明一些复杂的类型验证(List、Set、HttpUrl等)
    • 如果使用普通的Python list类型,无法对盛入的元素进行验证
    作者:iveBoy
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    索引的设计与使用
    字符集
    选择合适的数据类型
    java 23种设计模式 深入理解(转)
    进程间的通信方式
    SPRING的事务传播
    面向对象的五大基本原则
    J2EE中常用的名词解释
    JVM内存配置参数:
    域对象
  • 原文地址:https://www.cnblogs.com/shenjianping/p/14843994.html
Copyright © 2020-2023  润新知