FastApi学习总结

官网学习教程:https://fastapi.tiangolo.com/zh/

一、为什么要使用 FastAPI; FastAPI有哪些特点?

1.快速:

拥有非常高的性能,归功于 Starlette 和 Pydantic;Starlette 用于路由匹配,Pydantic 用于数据验证

Starlette

       1.官网使用介绍: https://www.starlette.io/

       2.什么是 Starlette?

        Starlette 是一种轻量级的 ASGI 框架/工具包,是构建高性能 asyncio 服务的理想选择。

       3.为什么使用 Starlette?

        Starlette 是目前测试最快的 Python 框架。只有 Uvicorn 超越了它,Uvicorn 不是框架,而是服务器。

       4.其他

        Starlette 提供了所有基本的 Web 微框架功能。但是它不提供自动数据验证,序列化或API 文档。

        自动数据验证由 Pydantic负责, API文档则是 OpenAPI  

        FastAPI 使用它来处理所有核心 Web 部件, 类 FastAPI 本身直接继承Starlette。

因此,使用 Starlette 可以执行的任何操作,都可以直接使用 FastAPI 进行。

        即路由匹配规则之内的 具体实现都要看 Starlette源码

Pydantic

       1.官网使用介绍:

https://pydantic-docs.helpmanual.io/

       2.什么是Pydantic?

            Pydantic 是一个库,基于Python类型提示来定义数据验证,序列化和文档(使用JSON模式)。

       3.为什么使用 Pydantic?

            FastAPI使用它来处理所有数据验证, 除了pydantic库之外,像是valideer库、marshmallow库、trafaret库以及cerberus库等都可以完成相似的功能,但是相较之下,pydantic库的执行效率会更加优秀一些。

Uvicorn

1.官网使用介绍

    [https://www.uvicorn.org/](https://www.uvicorn.org/)

2.什么是 Uvicorn?

Uvicorn 是基于 uvloop 和 httptools 构建的如闪电般快速的 ASGI 服务器。它不是Web框架,而是服务器

2.开发效率:

功能开发效率提升 200% 到 300%

  2.1 路径参数/查询参数/Body参数 的提取与校验都被最大程度的支持

  2.2 依赖注入能较大程度的复用公共逻辑

3.减少 bug:

减少 40% 的因为开发者粗心导致的错误

Pydantic提供的 数据验证功能与 类型注释的结合能有效的减少BUG

4.智能:

内部的类型注解非常完善,编辑器可处处自动补全

支持Pydantic的IDE(编辑器)都能很好的支持补全等操作

5.简单:

框架易于使用,文档易于阅读

6.简短:

使代码重复最小化,通过不同的参数声明实现丰富的功能

  依赖注入

7.健壮:

可以编写出线上使用的代码,并且会自动生成交互式文档

8.标准化:

兼容 API 相关开放标准

二、FastAPI开发的相关依赖

1. Python

Python的版本要大于等于 3.6

2. 其他模块依赖

pip install fastapi; 该操作会自动安装 Starlette 和 Pydantic

pip install uvicorn; uvicorn 是运行相关应用程序的服务器

pip install fastapi[all]; 一步到位, 所有依赖全部安装

三、交互式文档

FastAPI 会自动提供一个类似于 Swagger 的交互式文档,我们输入 “localhost:port/docs” 即可进入。

交互式文档会显示 接口API的相关内容, 并且可以自定义接口信息, 具体如何操作, 后续再更新

四、路由相关

1.路由匹配规则

1.1 普通路由匹配

在一个 路由定义模块中, 若一个URL可以匹配上多个 自定义路由的情况下

会优先匹配到 先定义的那一个。

举个例子:

# coding: utf-8
from fastapi import FastAPI
import uvicorn

app = FastAPI()

@app.get("/users/me")
async def read_user_me():
    return {"user_id": "the current user"}

@app.get("/users/{user_id}")
async def read_user(user_id: int):
    return {"user_id": user_id}

if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8080)

因为路径操作是按照顺序进行的,所以这里要保证 /users/me /users/{user\_id} 的前面,否则的话只会匹配到 /users/{user\_id},此时如果访问 /users/me,那么会返回一个解析错误,因为字符串 “me” 无法解析成整型。

2.APIRouter路由组

APIRouter主要是给路由加前缀, 方便于在项目中 对于不同模块或者不同类别的接口 加一个统一的前缀进行绑定, 有点类似分组/文件夹的意思

APIRouter: 有点类似于GO中gin框架中的路由组的概念, 所以这里写的是路由组

使用方式如下:

1.在app具体的模块中

# app/user/user_login.py
from fastapi import APIRouter

user_router = APIRouter(prefix="/v1/users")

# 以后访问的时候要通过 /v1/users/login     来访问
@router.get("/login")
async def v1():
    return {"message": "hello world"}

2.在项目启动加载的 main.py中:

# main.py
from fastapi import FastAPI
from app.user.user_login import user_router
import uvicorn

app = FastAPI()

# 将 user_router 注册到 app 中,相当于 Flask 中的 register_blueprint
app.include_router(router)

if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8080)

3.禁用一个路由:

参数 include_in_schema 设置为 False

@app.get(“/items/“, include_in_schema=False)

但是这个 禁用路由仅仅只是在 docs页面隐藏该 接口 不显示, 实际上并不影响其调用和真正使用。只是接口文档处对外界不可知~

五、请求参数获取

PS: 这里只介绍参数获取, 对于参数校验的写法, 详见第六章

5.1 获取路径参数

路径参数的声明:

路径参数声明在url中, 并且用一对花括号{user\_id}包裹; eg. @app.get("/user/{user\_id}")

对于多个 路径参数来说, 用多个 {} 包裹即可;eg. @app.get("/user/{part\_id}/{user\_name}")

路径参数的获取:

@app.get("/user/{part_id}/{user_name}")
async def get_user(part_id: str, user_name: str):
    """在路由函数中定义的两个路径参数 part_id/user_name
        只要路由函数中的 参数名能与之对应(一模一样), 
        那么在这里路由函数获取的 part_id/user_name就是路径参数上对应的 part_id/user_name
       在路由函数中 这两个参数的顺序没有要求
    """
    return {"part_id": part_id, "user_name": user_name}

对于特殊路径参数的获取, 详见 5.4 获取path类型参数

5.2 获取params参数

查询参数不需要声明, 如果函数中定义了不属于路径参数的参数时,那么它们将会被自动解释会查询参数。

方式一: 默认方式

@app.get("/user/{user_id}")
async def get_user(user_id: str, name: str, age: int):
    """我们在函数中参数定义了 user_id、name、age 三个参数
       显然 user_id 和 路径参数中的 user_id 对应,然后 name 和 age 会被解释成查询参数
       这三个参数的顺序没有要求,但是一般都是路径参数在前,查询参数在后

       PS: 这里做了 类型注释, 因此传进来的 参数会经过 是否必要性检测即参数类型检测
               这里的声明表示, params参数 name/age 是必填参数, 没有传会抛错
            虽然params参数的数据框架在接收的时候, value都是 str类型的, 但是这里声明 age: int时, 
            age: '10' 会被强转为 age: 10; 但是对于 age: 'test' 这类的参数 则会报类型错误~
    """
    return {"user_id": user_id, "name": name, "age": age}

方式二: 使用Query 类型注释

@app.get("/user/{user_id}")
async def get_user(user_id: str, name: str=Query(...), age: int=Query(...)):
    return {"user_id": user_id, "name": name, "age": age}

5.3 获取body参数

5.3.1 获取json类型 body参数(单个请求体)

方式一: 使用Body类型

from datetime import datetime, time, timedelta
from typing import Optional
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: Optional[datetime] = Body(None), # 这里的 Body类型注释 表明了获取的是一个 body参数
end_datetime: Optional[datetime] = Body(None),
repeat_at: Optional[time] = Body(None),
process_after: Optional[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,
}

方式二: 继承BaseModel (Pydantic提供)

如果参数的类型被声明为一个 Pydantic 模型,它将被解释为请求体。

from typing import Optional, List
from fastapi import FastAPI, Request, Response
from pydantic import BaseModel
import uvicorn

app = FastAPI()


class Girl(BaseModel):
“””数据验证是通过 pydantic 实现的,我们需要从中导入 BaseModel,然后继承它”””
name: str
age: Optional[str] = None
length: float
hobby: List[str] # 对于 Model 中的 List[str] 我们不需要指定 Query(准确的说是 Field)


@app.post(“/girl”)
async def read_girl(girl: Girl):
# girl 就是我们接收的请求体,它需要通过 json 来传递,并且这个 json 要有上面的四个字段(age 可以没有)
# 通过 girl.xxx 的方式我们可以获取和修改内部的所有属性
return dict(girl) # 直接返回 Model 对象也是可以的

5.3.2 获取json类型 body参数(多个请求体)

上面只是接收一个请求体的情况, 如果是接收两个请求体呢?

class Girl(BaseModel):
    name: str
    age: Optional[str] = None

class Boy(BaseModel):
    name: str
    age: int

@app.post("/boy_and_girl")
async def read_boy_and_girl(girl: Girl,
                            boy: Boy):
    return {"girl": dict(girl), "boy": dict(boy)}

对于上述这种写法, 在传入body数据时, 在json串中 应该有两个子json串,且其key分别为 girl/boy

如何理解?

实际上对于 继承了 BaseModel或者 路由函数中 直接使用 Body类型进行声明的 参数, 框架都会接收为 body参数, 这些body参数可以看做是关键字参数, 但是当 只有一个 BaseModel类型时, 就可以不使用关键字进行识别

上述案例的请求体示例如下:

{
    "girl": {"name": "新垣结衣", "age": 18},
    "boy": {"name": "星野源", "age": 18}
}

PS: 其实上述写法, 也可以只是用一个 请求体来表示( 即这种 BaseModel的写法是可以多层嵌套的~)

class Girl(BaseModel):
    name: str
    age: Optional[str] = None

class Boy(BaseModel):
    name: str
    age: int

class BoyGirl(BaseModel):
    girl: Girl
    boy: Boy

@app.post("/boy_and_girl")
async def read_boy_and_girl(boy_girl: BoyGirl):
    return dict(boy_girl)

5.3.3 获取from表单数据

方式一: 使用 Form 类型

from fastapi import FastAPI, Form

app = FastAPI()
@app.post("/login")
async def user_login(username: str = Form(...),        # 这里的Form类型 表明接收的是 form表单的数据
                   password: str = Form(...)):
    return {"username": username, "password": password}

方式二:使用request

from fastapi import FastAPI, Request

app = FastAPI()
@app.post("/girl")
async def girl(request: Request):
    # 此时 await request.json() 报错,因为是通过 data 参数传递的,相当于 form 表单提交
    # 如果是通过 json 参数传递,那么 await request.form() 会得到一个空表单
    form = await request.form()
    return [form.get("name"), form.getlist("age")]

PS: python如何发送 form表单数据(使用 data参数)

requests.post(url, data={"name": "test1", "password": "test2"})

5.3.4 获取文件上传数据

1.获取单个文件(使用 File类型)

from fastapi import FastAPI, File, UploadFile

app = FastAPI()
@app.post("/file1")
async def file1(file: bytes = File(...)):        # 这里使用的是 File类型 接收的是一个 bytes 字节流
    return f"文件长度: {len(file)}"

@app.post("/file2")
async def file1(file: UploadFile = File(...)):        # 这里接收的是 一个文件句柄对象, 这两种方式都可以获取到上传的文件
    return f"文件名: {file.filename}, 文件大小: {len(await file.read())}"

2.获取多个文件

@app.post("/file")
async def file(files: List[UploadFile] = File(...)):
    """指定类型为列表即可"""
    for idx, f in enumerate(files):
        files[idx] = f"文件名: {f.filename}, 文件大小: {len(await f.read())}"
    return f

PS: Python如何上传文件?

首先如果想使用文件上传功能,那么你必须要安装一个包 python-multipart,直接 pip install python-multipart 即可。

# 上传单个文件
requests.post(url, files={"file": open(r"/file_path/file_name", "rb")})

# 上传多个文件
requests.post(url, files={"file": [
            ("files", open(r"/file_path1/file_name1"),
               ("files", open(r"/file_path2/file_name2"),
            ("files", open(r"/file_path3/file_name3")
]})

5.4 获取path类型参数

假设我们有这样一个路由:/files/{file\_path},而用户传递的 file_path 中显然是可以带 / 的,假设 file_path 是 /root/test.py,那么路由就变成了 /files//root/test.py,显然这样子进行路由匹配就会出问题。

这是我们需要声明 file_path 的类型为 path, eg. @app.get(“/files/{file_path:path}”)

方式一: 声明path类型

from fastapi import FastAPI

app = FastAPI()


# 声明 file_path 的类型为 path,这样它会被当成一个整体
@app.get(“/files/{file_path:path}”)
async def get_file(file_path: str):
return {“file_path”: file_path}

方式二: 使用Path 类型注释

from fastapi import FastAPI, Path
@app.get("/items/{item-id}")
# 这里用 Path类型来接收, 也会将后面的整体作为一个path.
# 这里的 alias表示 重命名, 也就是将 路由函数中的 变量 item_id 与 路由中的 item-id 对应上.
async def read_items(item_id: int = Path(..., alias="item-id")):
    return {"item_id": item_id}

PS: 也就是说, 当路径参数中 声明了 一个参数为 Path类型, 那么这个参数后面不能在接其他的路径参数。即 @app.get("/files/{file\_path:path}/{user\_id}") 这样的写法, user_id就获取不到~

5.5 多种请求参数混合获取

5.5.1 路由函数中使用类型区别

方式一: 直接使用内置方式获取

1.如果在路径中也声明了该参数,它将被用作路径参数

2.如果参数属于单一类型(比如 int、float、str、bool 等)它将被解释为查询参数。

3.如果参数的类型被声明为一个 Pydantic 模型,它将被解释为请求体。

from fastapi import FastAPI
from pydantic.main import BaseModel

app = FastAPI()

class Girl(BaseModel):
    name: str
    age: int

@app.get("/user/{user_id}")
async def get_user(user_id: str, name: str, age: int, girl: Girl):
    # user_id 为路径参数
    # name/age 为params参数
    # girl 为body参数
    return {"user_id": user_id, "name": name, "age": age, "girl": girl.dict()}

方式二: 使用 类型注释来获取

Path(路径参数)/Query(params参数)/Body(json请求体)/Form(form表单请求体)/File(文件)

Cookie(cookie参数)/Header(请求头)

关于 Path(路径参数)/Query(params参数)/Body(json请求体)/Form(form表单请求体)/File(文件)的使用方式, 上面已经有过展示, 这里不再赘述

Cookie:

1.声明:ads_id: Optional[str] = Cookie(None)

Header:

1.声明: user_agent: Optional[str] = Header(None)

2.默认情况下, Header 将把参数名称的字符从下划线 (_) 转换为连字符 (-) 来提取并记录 headers.

3.设置Header的参数 convert_underscores 为 False, 可以取消上述的 下划线到连字符的自动转换

4.headers 是大小写不敏感的

5.声明多个header: x_token: Optional[List[str]] = Header(None) –> 例: 使用header传输 cookie的情况

PS:

Cookie 、Header、Path 、Query是兄弟类,它们都继承自公共的 Param 类 –> 很明显Body/File/Form不是

5.5.2 使用request对象获取

任何一个请求都对应一个 Request 对象,请求的所有信息都在这个 Request 对象中

获取参数方式如下:

@app.get("/girl/{user_id}")
async def read_girl(user_id: str,
                    request: Request):
    """路径参数是必须要体现在参数中,但是查询参数可以不写了
       因为我们定义了 request: Request,那么请求相关的所有信息都会进入到这个 Request 对象中"""
    header = request.headers  # 请求头
    method = request.method  # 请求方法
    cookies = request.cookies  # cookies
    query_params = request.query_params  # 查询参数
    body_data = await request.json()  # 获取json请求体, 该函数本质上步骤为: body = await self.body() + self._json = json.loads(body)
    return {"name": query_params.get("name"), "age": query_params.get("age"), "hobby": query_params.getlist("hobby")}

5.5.3 参数顺序

路径参数应该在查询参数的前面,尽管 FastAPI 没有这个要求,但是这样写明显更舒服一些。但是问题来了,如果路径参数需要指定别名,但是某一个查询参数不需要,这个时候就会出现问题

@app.get("/items/{item-id}")
async def read_items(q: str,
                     item_id: int = Path(..., alias="item-id")):

    return {"item_id": item_id, "q": q}

显然此时 Python 的语法就决定了 item_id 就必须放在 q 的后面,当然这么做是完全没有问题的,FastAPI 对参数的先后顺序没有任何要求,因为它是通过参数的名称、类型和默认值声明来检测参数,而不在乎参数的顺序。但此时我们就要让 item_id 在 q 的前面要怎么做呢?

@app.get("/items/{item-id}")
async def read_items(*, item_id: int = Path(..., alias="item-id"),
                     q: str):

    return {"item_id": item_id, "q": q}

通过将第一个参数设置为 *,使得 item_id 和 q 都必须通过关键字传递,所以此时默认参数在非默认参数之前也是允许的。当然我们也不需要担心 FastAPI 传参的问题,你可以认为它所有的参数都是通过关键字参数的方式传递的。

六、数据校验

6.1 类型注释的数据校验方式

Path(路径参数)/Query(params参数)/Body(json请求体)/Form(form表单请求体)/File(文件)/Cookie(cookie参数)/Header(请求头)

上述这些 类在用于 类型注释时, 使用方式基本都差不多, 只是在某些方面有一些细微的差别

6.1.1 公共校验部分

这里以Query为例

Query(
            None,
            alias="item-query",
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
            max_length=50,
            regex="^fixedquery$",
            deprecated=True,
        )

        1.修改额外的原数据:
            alias - 起别名
            title/description - 文档描述
            deprecated - 参数弃用标识

        2.用于校验的参数:    
            min_length/max_length    长度匹配
            regex        正则匹配

        3.数值校验:
            gt/ge/lt/le: 大于/大于等于/小于/小于等于 定义为float类型

        4.设置必填/非必填/默认值
        Query(...)    - 设置为必填
         Query(None) - 设置为非必填
      Query("test") - 设置默认值 test

     5.接收列表
         q: Optional[List[str]] = Query(None) 定义 query参数为必填的list, 即能匹配 xxx_url?q=foo&q=bar ---> q=['foo', 'bar']

     6.指定数据接收类型
         q:  Optional[str] = Query(...)
      q: str    

      这里支持的数据类型:
      int/float/str/bool/list/dict/set
      UUID/datetime.datetime(2008-09-15T15:53:00+05:00)/datetime.date(2008-09-15)
      datetime.time(14:23:55.003)/datetime.timedelta(时间戳秒数)/bytes/Decimal(框架内可当做float使用)

         支持指定多种数据类型:
          user_id: Union[int, str]

举个例子:

@app.get("/user")
async def check_length(
        password: str = Query("satori", min_length=6, max_length=15, regex=r"^satori")
):
    """此时的 password 默认值为 'satori',并且传递的时候必须要以 'satori' 开头
       但是值得注意的是 password 后面的是 str,不再是 Optional[str],因为默认值不是 None 了
       当然这里即使写成 Optional[str] 也是没有什么影响的
    """
    return {"password": password}


@app.get(“/items”)
async def read_items(
a1: str = Query(…), # … 设置为必填参数
a2: List[str] = Query(…), # 查询参数变成一个列表; 对应于 url后 a=1&a=2的情况
b: List[str] = Query(…)
):
return {“a1”: a1, “a2”: a2, “b”: b}

@app.get("/items")
async def read_items(
        # item1 必须大于 5
        item1: int = Query(..., gt=5),        # 数值检测
        # item2 必须小于等于 7
        item2: int = Query(..., le=7),
        # item3 必须必须等于 10
        item3: int = Query(..., ge=10, le=10)
):
    return {"item1": item1, "item2": item2, "item3": item3}

6.1.2 各类型特殊部分

Path:

1.路径参数总是必须的, 所以应表示为Path(…), 即使写为了 Path(None), 也还是一个必选参数

Header:

1.默认情况下, Header 将把参数名称的字符从下划线 (_) 转换为连字符 (-) 来提取并记录 headers.

2.设置Header的参数 convert_underscores 为 False, 可以取消上述的 下划线到连字符的自动转换

3.headers 是大小写不敏感的

6.1.3 特殊检验类型 Filed

Filed与BaseModel一样, 由pydantic模块提供

Filed类型的用法与 Query一致, 唯一的区别就是Filed可以对所有类型的 参数进行参数校验,而不像Query/Body这类参数, 在校验时, 只能获取对应的参数。

如果有个 username参数即在 params参数中会出现, 在body参数中也会出现, 甚至于在 response 中也可能会出现, 此时使用 Filed进行参数验证就比较合适

Filed的使用 示例见 6.3

6.2 变量类型声明的特殊情况

6.2.1 默认值与声明值可不同

@app.get("/user/{user_id}")
async def get_user(user_id: str, name: str = "UNKNOWN", age: int = "哈哈哈"):    # 这里的 age 需要接收一个整型,但是默认值却是一个字符串
    return {"user_id": user_id, "name": name, "age": age}

这种情况下传递的 age 依旧需要整型,只不过在不传的时候会使用字符串类型的默认值,所以类型和默认值类型不同的时候也是可以的

6.2.2 同一个变量指定多个类型

from typing import Union, Optional

@app.get("/user/{user_id}")
async def get_user(user_id: Union[int, str], name: Optional[str] = None):
    """通过 Union 来声明一个混合类型,int 在前、str 在后。会先按照 int 解析,解析失败再变成 str
       然后是 name,它表示字符串类型、但默认值为 None(不是字符串),那么应该声明为 Optional[str]"""
    return {"user_id": user_id, "name": name}

6.2.3指定数据接收类型

q: Optional[str] = Query(…)

q: str

这里支持的数据类型:

int/float/str/bool/list/dict/set

UUID/datetime.datetime(2008-09-15T15:53:00+05:00)/datetime.date(2008-09-15)

datetime.time(14:23:55.003)/datetime.timedelta(时间戳秒数)/bytes/Decimal(框架内可当做float使用)

支持指定多种数据类型:

user_id: Union[int, str]

6.3 BaseModel校验方式

这里是用了 Filed做示例, 如果是 Body参数, 使用Body也可,其他的类推~

示例如下:

class UserIn(BaseModel):
    """用户信息 请求格式"""
    name: str = Field(..., min_length=1, max_length=64)
    password: str = Field(..., min_length=1, max_length=32)
    nickname: str = Field(None, max_length=64)
    phone: str = Field(None, max_length=16, regex=r'^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$')    # 正则匹配
    email: str = Field(None, max_length=32, regex=r'^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$')

class UserOut(BaseModel):
    """用户信息响应格式"""
    id: str = Field(..., max_length=32)
    name: str = Field(..., min_length=1, max_length=64)
    nickname: str = Field(None, max_length=64)
    last_login_address: str = Field(None, max_length=32)
    last_login_time: datetime.datetime = Field(None)    # 指定datetime类型
    login_counts: int = Field(0)        # 设置默认值 0
    phone: str = Field(None, max_length=16)        # 设置为非必须字段
    email: str = Field(None, max_length=32)
    enabled: int = Field(1)
    is_deleted: int = Field(0)

6.4 HTTP验证 security

通过输入用户名和密码来确认身份

from fastapi import FastAPI, Depends
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import uvicorn

app = FastAPI()

security = HTTPBasic()

@app.get("/index")
async def index(credentials: HTTPBasicCredentials = Depends(security)):
    return {"username": credentials.username, "password": credentials.password}

当用户访问 /index 的时候,会提示输入用户名和密码:

image

输入完毕之后,信息会保存在 credentials,我们可以获取出来进行验证。

6.5 其他

6.5.1 使用枚举

如果我们希望有一个参数的取值范围只能是固定的 几个值, 可以使用枚举类型

示例如下:

from enum import Enum

class Gander(str, Enum):
    type1 = "boy"        # 这里 type1 只是一个随意变量, 没有任何其他含义
    type2 = "girl"

@app.get("/users/{gander_type}")
async def get_user(gander_type: Gander):
    return {"gander_type": gander_type}    # 这里的 gander_type 就只能是 boy/girl; 否则就会抛错

6.5.2 bool 类型自动转换

对于布尔类型,FastAPI 支持自动转换

@app.get("/{flag}")
async def get_flag(flag: bool):
    return {"flag": flag}        # 这里 flag 为 True/true/on/yes/1 均会转换为 True; 0/False/false/off/no 均会转为 False

 上一篇
缓存淘汰算法:LRU、TTL 缓存淘汰算法:LRU、TTL
LRU CacheLRU(Least recently used, 最近最少使用),其核心思想是:如果一个数据最近一段时间被访问过,那么以后被访问的几率越高,如果一个数据最近一段时间没有被访问过,那么将来被访问的几率越小;当内存超过限制时,
2022-07-14
下一篇 
Supervisor进程管理工具 Supervisor进程管理工具
一、supervisor简介Supervisor是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。它是通过fork/exec的方式把这些被管理的进程当作supe
2021-10-18
  目录