推荐
关注
TOP
Message

FastAPI

https://fastapi.tiangolo.com/zh/ 中文文档

https://pydantic-docs.helpmanual.io/ pydantic 文档
https://tortoise-orm.readthedocs.io/en/latest/getting_started.htmltortoise-orm异步sqlalchemy

1. 前期准备

1. 安装

首先需要安装两个库

fastapi 和 uvicorn

pip install fastapi uvicorn -i [https://pypi.tuna.tsinghua.edu.cn/simple/](https://pypi.tuna.tsinghua.edu.cn/simple/)

一定要加 -i  不然 安装有可能失败 所以一定要换源

2. 运行

第一种 直接运行

from fastapi import FastAPI
import uvicorn

app = FastAPI()

if __name__ == '__main__':
    uvicorn.run(app)

但此时运行 会报错 因为没有找到资源

第二种 终端运行

uvicorn [文件名]:app

uvicorn参数解释:

  • main: 指定主程序文件main.py文件, 如果main.py文件改成test.py 则命令也需要改为uvicorn test:app
  • app:在main.py中使用app = FastAPI()创建的对象
  • --host:远程主机ip,如果是本地则可以不要这个参数
  • --host:端口号
  • --reload:在修改源代码后程序会自动重新加载不用退出重新启动

3. 请求方式

1. get

这个是最常见的请求形式
(1) url参数
修改main.py

from fastapi import FastAPI
app = FastAPI()
# url参数定义在这个修饰器里面
@app.get("/{url}")
def read_root():
    return {"Hello": "World"}

我们在@app.get中定义了url参数,那么我们下次get请求时就不能直接输入http://127.0.0.1:8000而是http://127.0.0.1:8000/xxxx(xxxx表示任意字符串)

(2) param参数
修改main.py

from fastapi import FastAPI
app = FastAPI()
# url参数可以和param重合
@app.get("/{url}")
def read_root(url: str, parms_1: str, parms_2: str=None):
    return {'url地址是: ': url, "parms_1参数是 ": parms_1, "parms_2参数是 ": parms_2}

这是输入http://192.168.30.17:8000/anywords?parms_1=abc&parms_2=def就可以了,这里我使用的是远程服务器,因此我指定了ip和端口

parm参数

说明:

1.如果有parm参数则在地址后面加 ?,多个parm参数使用 & 连接

2.return中单引号和双引号没有区别

3.从上面可以看出url参数可以和parm参数合一,这是第一个url参数就不需要?url=,而是直接跟在端口80000/后面

2. post

post方法参数不是直接接在url后面,安全性会好一些
使用post时需要继承BaseModel这个类
(1)body参数

from fastapi import FastAPI
from pydantic import BaseModel    #fastapi的一个依赖,需要从pydantic中引入
app = FastAPI()
class Args(BaseModel):    #继承BaseModel
    data_str: str    #定义一个字符串型参数
    data_int: int    #定义一个整形参数
    data_list: list  #定义一个列表
        
@app.post("/test_post")
async def postEchoApi(args:Args):    #设置刚才定义的参数
    dict_args = args.dict() # 也可以转化为字典
    return {"str data":args.data_str, 
            'int data': args.data_int, 
            'list data':args.data_list, 
            'args 数据类型': str(type(args))}

postman

测试post的时候就用postman就可以了,注意是在Body选择Raw在选择Json形式传入参数

2. 在服务器部署fastapi

第一种使用方式

uvicorn main:app --host '0.0.0.0' --port 8000 --reload --workers 1

第二种使用方式(推荐)
Gunicorn是一个成熟的、功能齐全的服务器和流程管理器。
Uvicorn包含一个Gunicorn worker类,允许您运行ASGI应用程序,具有Uvicorn的所有性能优势,同时还为您提供了Gunicorn的全功能流程管理。
这允许您动态地增加或减少工作进程的数量,正常地重新启动工作进程,或者在不停机的情况下执行服务器升级。
对于生产部署,我们建议将gunicorn与uvicorn worker类一起使用。

gunicorn main:app -b 0.0.0.0:8000 -w 1 -k uvicorn.workers.UvicornWorker

3. 请求参数和验证

1. helllo world 接口给后端COVID-19感染数据

首先运行一个简单的fastAPI程序,实现返回 “hello,world”的接口,进一步 继承Pydantic的Basemodel规范请求体数据格式和和类型。通过查询参数和请求体传递城市,所在国家,是否有感染病例的信息,讲解FastAPI框架的脚本开发方法,同步和异步函数的编写 装饰器,url路由,HTTP方法。

2. FastAPI的交互文档 - SwaggerUi 和ReDoc

API分档分为 SwaggerUi 和ReDoc
SwaggerUI : 0.0.0.0:8080/docs

ReDoc: http://0.0.0.0:8080/redoc
这个文档只能看不能用 不太好用

3. 路径参数的数据解析 验证

路径参数的类型,错误检查 自动填充没数据转换,解析验证(包括数字大小写范围的验证),参数别名,API交互文档的传参演示

from typing import Optional, List

from fastapi import APIRouter, Path, Query
from enum import Enum

from pydantic import BaseModel, Field

app01 = APIRouter()

"""
Path Parameters  and Number validation 路径参数与数字验证验证
"""

@app01.get("/path/parameters")
async def path_params01():
    return {"message": "This is a message"}

@app01.get("/path/{parameters}")  # 函数的顺序 就是路由的顺序
async def path_params02(parameters):
    return {"message": f"This is a {parameters}"}

class CityName(str, Enum):
    BeiJing = "beijing china"
    ShangHai = "shanghai china"

# 还可以定义枚举类型
@app01.get("/enum/{city}")
async def latest(city: CityName):
    if city == CityName.BeiJing:
        return {"cityName": city, "count": 1111, "death": 10}
    elif city == CityName.ShangHai:
        return {"cityName": city, "count": 2222, "death": 22}

# 通过path path 传递路径  :path  代表 这个是个路径传值
@app01.get("path/{file_path:path}")
async def file_path(file_path: str):
    return {"filePath": f"{file_path}"}

# 验证参数
@app01.get("path_/{num}")
def path_params_verify(num: int = Path(..., title="Your number", description="不可描述", ge=1, le=10)):
    return num

4. 查询参数和字符串的验证 Query Parameters and String Validation

讲解查询参数的传参方式,类型转换,多路径参数和查询参数的使用。必填查询参数 骂你一个能选择的数据源,查询今日全球covid-19感染数据的接口

# 给了参数值就是必填参数 没给就可填可不填
@app01.get("query")
def page_limit(page: int = 1, limit: Optional[int] = None):
    if limit:
        return {"page": page, "limit": limit}
    return {"page": page}

# 布尔值的转换
@app01.get("query/bool/conversion")
def bool_conversion(params: bool = False):
    return f"{params}"

# 查询多个参数的列表
@app01.get("query/string/conversion")
def string_conversion(
        value: str = Query(..., min_length=8, max_length=16, regex="^a"),
        values: List[str] = Query(default=["v1", "v2"], alias="alias_name")
):
    return {"value": f"{value}", "values": f"{values}"}

5. 请求体以及混合参数

用Pydantic的BaseModel和Field类定义请求体数据格式和类型。如何定义多个请求体,请求体和路径参数,查询参数的混合使用。

class CityInfo(BaseModel):
    # example 是注解 的功能
    name: str = Field(..., example="BeiJing")
    country: str
    country_code: str = None
    country_population: int = Field(default=800, title="人口数量", description="国家的人口数量", ge=800)

    class Config:
        schema_extra = {
            "example": {
                "name": "shanghai",
                "country": "China",
                "country_code": "CN",
                "country_population": 1400000,
            }
        }

@app01.post("/request_body/city")
def city_info(city: CityInfo):
    print(city.name, city.country)
    return city.json()

@app01.put("/request_body/city/{name}")
def mix_city_info(
        name: str,
        city01: CityInfo,
        city02: CityInfo,
        confirmed: int = Query(ge=0, description="确诊人数", default=0),
        death: int = Query(ge=0, description="死亡人数", default=0),
):
    if name == "Shanghai":
        return {"Shanghai": {"confirmed": confirmed, "death": death}}
    return city01.dict(), city02.dict()

6. 如何定义数据格式嵌套的请求体Request Body - Nested Models

通过对Python类的继承,结合Typing 和Pydantic 的Field类,用嵌套的模型类定义数据格式嵌套的请求体

class Data(BaseModel):
    city: List[CityInfo] = None  # 这里就是定义 数据格式嵌套的请求体
    date: date  # 额外的数据类型 可以在官网查找到
    confirmed: int = Field(ge=0, default=0, description="确诊人数")
    death: int = Field(ge=0, default=0, description="死亡人数")
    recovered: int = Field(ge=0, default=0, description="治愈人数")

@app01.put("/request_body/nest")
def nested(data: Data):
    return data.json()

7.额外的参数和数据类型都有哪些?

schema.extra 属性定义额外的参数,其余数据类型一览。如
UUID
datetime
forzenset
bytes
decimal等

用fastiapi的ccokie类实现后端定义cookie的参数
用fastapi的header类定义后端定义请求头的参数。请求头参数自动转换功能介绍,请求头擦拭农户中重复key 如何处理

@app01.get("/cookie")
def cookie(cookie_id: Optional[str] = Cookie(None)):
    return {"cookie_id": cookie_id}

@app01.get("/header")
def header(user_agent: Optional[str] = Header(None), x_token: List[str] = None):
    return {"user_agent": user_agent, "x_token": x_token}

4. 响应数据和FastAPI配置

1. 响应模型 response model

响应模型的声明和调用
response_model 指定默认值或者响应模型。
response_model_exclude_unset
response_model_include_model_exclude
参数含义

class UserInfo(BaseModel):
    user_name: str
    password: str
    email: EmailStr
    mobile: str = "10086"
    address: str = None
    full_name: Optional[str] = None

class UserOut(BaseModel):
    user_name: str
    email: EmailStr
    mobile: str = "10086"
    address: str = None
    full_name: Optional[str] = None

users = {
    "user01": {"user_name": "zic1", "password": "1234555", "email": "hybpjx1@163.com"},
    "user02": {"user_name": "zic2", "password": "12345", "email": "hybpjx2@163.com", "mobile": "110"}
}

# response_model_exclude_unset=True 只封装 填入的值
@app02.post("/response_model", response_model=UserInfo, response_model_exclude_unset=False)
async def response_model(user: UserInfo):
    print(user.password)
    return users["user02"]

@app02.post(
    "/response_model/attributes",
    # response_model=UserInfo,
    # response_model=Union[UserInfo, UserOut]
    response_model = List[UserInfo],
    # response_model_include=["user_name", "email"],
    # response_model_exclude=["password"]
)
async def response_model_attr(user: UserInfo):
    # 可通过关键字删除密码  即使删除密码 依然可以成功
    # del user.password
    # 在使用  response_model = List[UserInfo] 时 需要返回一个列表
    return [user,user]
    # return user.dict()

2. 响应状态码 response status code

status_code 参数定义,fastapi。status快捷调用响应状态码的属性

@app02.post("status_code", status_code=200)
def status_code():
return {"status_code": 200}

# 实际上 status.HTTP_200_OK 就是200  
@app02.post("status_attribute", status_code=status.HTTP_200_OK)
def status_attribute():
print(status.HTTP_200_OK)
print(type(status.HTTP_200_OK)) # int类型
return {"status_attribute_status_code": status.HTTP_200_OK}

3. 数据处理 Form Data

导入FastAPI的form类,代码演示如何定义表单参数。

@app02.post("form_login")
def form_login(user_name: str = Form(...), password: str = Form(...)):
# 用Form类需要 安装 pip install python-multipart  Form 类的方法 类似于body query
return {"user_name": user_name, "password": password}

4. 单文件 多文件上传 以及参数详解 request File

file的UploadFile 类。以及其参数的定义,多文件的上传的实现。以解析COVID-19.csv文件数据为例
使用 upload_file的优势:

  1. 文件储存在内从中 当文件大于一个阀值 就写入磁盘总
  2. 适用于图片 视频 等
  3. 可以获取上传文件多元数据 内存 ,大小 上传时间 等
  4. 有文件对象多异步接口
  5. 上传多是python对象 由 read write 等方法
# @app02.post("/file")
# async def fileobj(file: bytes = File(...)):
@app02.post("/file")  # 上传多个文件
async def fileobj(file: List[bytes] = File(...)):
    """ 由于文件读写 是由bytes 读取 所以适合小内存多文件"""
    return {"file_size": len(file)}

@app02.post("/upload_file")
async def upload_file(files: List[UploadFile] = File(...)):
    """
    使用 upload_file的优势:
    1. 文件储存在内从中 当文件大于一个阀值 就写入磁盘总
    2. 适用于图片 视频 等
    3. 可以获取上传文件多元数据 内存 ,大小 上传时间 等
    4. 有文件对象多异步接口
    5. 上传多是python对象 由 read write 等方法
    :param file:
    :return:
    """
    for file in files:
        content = await file.read()
        print(content)

    return {"file_name": files[1].filename, "file_content_type": files[0].content_type}

5. FastAPI项目的静态文件配置

CSS/JS/Images/静态文件的配置 项目如何找到static文件

在目录工程下新建static
在main.py下新加如下代码

from fastapi.staticfiles import StaticFiles
# mount 表示 将某个目录下一个完全独立的应用挂载过来,这个不会在API交互文档中显示
app.mount(
	path="/static",
	app=StaticFiles(directory="static"),
	name="static")

6.路径操作配置(path,operation configuration)

Path Operation Configureation的概念,包括Response status Code,Tags,Summary and description, 文档描述符,相应描述,Deprecate参数

@app02.post(
    "/path_operation_configuration",
    response_model=UserOut,
    # tags=['path','operation','configuration'],# 这个Tags 和主程序中的一样 会显示在API文档中
    summary="this is a summary",
    description="this is a description",
    response_description="this is a response description",
    # deprecated=True,# 表示这个接口已经废弃
    status_code=status.HTTP_200_OK,
)
async def path_operation_configuration(user: UserIn):
    """
    Path Operation Configuration 路径操作配置
    :param user: 用户信息
    :return: 返回结果
    """
    return user.dict()

7. FastAPI应用的常见配置型

配置FasAPI 应用标题,描述,版本,tags的元数据,自定义openAPI和文档的url

app = FastAPI(
    title="FastAPI Tutorial and Coronavirus Tracker API Docs ",
    description="新管病毒 疫情跟踪器 API接口文档",
    version="1.0",
    docs_url="/docs",
    redoc_url="/redocs",
)

8. FastAPI框架的错误处理

HTTPException的使用,如何自定义异常处理器,给第六小结开发的COVID-19数据查询接口定义

"""
Handing Error 错误处理
"""


@app02.get("/http_exception", )
async def http_exception(city: str):
    if city != "Bei Jing":
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="City Not Found",
                            headers={"X-Error": "error"})
    return {"city": city}

重写异常处理

from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from fastapi.exceptions import HTTPException
from starlette.exceptions import HTTPException as star_HTTPException
@app.exception_handler(star_HTTPException)  # 重写异常处理
async def http_exception_handler(request, exc):
    """
    :param request: 这个参数不能省
    :param exc: 
    :return: 只是把原本是Json的response 转变成文本的response
    """
    return PlainTextResponse(str(exc.detail), status_code=exc.status_code)


@app.exception_handler(RequestValidationError)  # 重写请求验证的处理器
async def validation_exception_handler(request, exc):
    """
    :param request: 这个参数不能省
    :param exc:
    :return:
    """
    return PlainTextResponse(str(exc), status_code=400)

5. FastApi的依赖注入系统

“依赖注入”是指在编程中,为了保证代码成功运行,先导入或者声明器所需要的“依赖”,如子函数,数据库链接等等。

  • 提高代码复用率
  • 共享数据库链接
  • 增强安全,认证和角色管理。(权限控制方面)

FastAPI的兼容性

  • 所有的关系型数据库 支撑nosql数据库
  • 第三方的包和API
  • 认证和授权系统
  • 响应数据注入系统

1. Dependencies 函数作为依赖项 创建和导入和声明依赖

async def common_parameters(q: Optional[str] = None, page: int = 1, limit: int = 10):
    return {"q": q, "page": page, "limit": limit}


@app03.get("/dependency01")  # 依赖的导入和声明
async def dependency01(commons: dict = Depends(common_parameters)):
    return commons


# 不区分 async 和 def 都可以调用
@app03.get("/dependency02")
def dependency02(commons: dict = Depends(common_parameters)):
    return commons

2. classes as dependencies 类作为依赖性

fake_items_db = [
    {
        "item_name": "Foo",
    },
    {
        "item_name": "Bar",
    },
    {
        "item_name": "Nav",
    },
]


class CommonQueryParams:
    def __init__(self, q: Optional[str] = None, page: int = 1, limit: int = 10):
        self.q = q
        self.page = page
        self.limit = limit


@app03.get("/classes_as_dependencies")
# 三种写法
# async def classes_as_dependencies(common: CommonQueryParams = Depends(CommonQueryParams)):
# async def classes_as_dependencies(common: CommonQueryParams = Depends()):
async def classes_as_dependencies(common=Depends(CommonQueryParams)):
    response = {}
    if common.q:
        response.update({"q": common.q})
    items = fake_items_db[common.page:common.page+common.limit]
    response.update({"items": items})
    return response

3. Sub-dependencies 子依赖

def query(q: Optional[str] = None):
    return q


def sub_query(q: str = Depends(query), last_query: Optional[str] = None):
    if q:
        return q
    else:
        return last_query


@app03.get("/sub_dependency")
async def sub_dependency(final_query: str = Depends(sub_query, use_cache=True)):
    # use_ache表示 当有多个依赖有一个共同的子依赖时,每次request请求只会调用子依赖一次,多次调用将从缓存中调用。
    return {"sub_dependency":final_query}

4. Dependencies in path operation decorators 路径操作装饰器中的多依赖

async def verify_token(x_token: str = Header(...)):
    """没有返回值的子依赖"""
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="X-Token header invalid")
    return x_token


async def verify_key(x_key: str = Header(...)):
    """有返回值的子依赖 但是返回值不会被调用"""
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="X-Key header invalid")
    return x_key


@app03.get("/dependency_in_path_operations", dependencies=[Depends(verify_token), Depends(verify_key)])
async def dependency_in_path_operations():
    return [
        {"user": "user01"},
        {"user": "user02"},
        {"user": "user03"},
    ]

5. Global Dependencies 全局依赖

    1. 通过某个单个路由 实现单个路由全局
# 使得全局都要 经过上述定义的两个以来 key和token
app03 = APIRouter(dependencies=[Depends(verify_token), Depends(verify_key)])
    1. 全局 整个项目
# 使得全局都要 经过上述定义的两个以来 key和token
app = FastAPI(
     dependencies=[Depends(verify_token), Depends(verify_key)]
)

6. Dependency with yield 带yield的依赖

需要python 3.7 版本才支持,不然的话 需要安装很多的依赖,async-exit-stack async-generator
以下是伪代码

async def get_db():
    db = "db_connection"
    try:
        yield db
    finally:
        db.endswith("db_close")


async def dependency_a():
    dep_a = "generate_dep_a()"
    try:
        yield dep_a
    finally:
        dep_a.endswith("dep_a_close")


async def dependency_b(dep_a: Depends(dependency_a)):
    dep_b = "generate_dep_b()"
    try:
        yield dep_b
    finally:
        dep_b.endswith(dep_a)


async def dependency_c(dep_b: Depends(dependency_b)):
    dep_c = "generate_dep_c()"
    try:
        yield dep_c
    finally:
        dep_c.endswith(dep_b)

6. OAuth2.0的授权模式

  • 授权码授权模式(Authorization Code Grant)
  • 隐式授权模式(Implicit Grant)
  • 密码授权模式(Resource Owner Password Credentials Grant)

在主程序中设置
app.include_router(app04, prefix="/test4", tags=["安全 认证与授权"])

"""
OAuth2PasswordBearer 是接受Url作为参数的一个类: 客户会向该URL发送UserName和Password 参数,然后得到一个Token值
OAuth2PasswordBearer 不会创建相应的URL路径操作,只是指明客户端用来请求Token的URL地址
"""
oauth2_schema = OAuth2PasswordBearer(tokenUrl="/test4/token")  # 请求token的Url地址


@app04.get("oauth2_password_bearer")
async def oauth2_password_bearer(token: str = Depends(oauth2_schema)):
   return {"token": token}

1. 实现 oauth2_password_bearer认证


fake_users_db = {
   "john snow": {
       "username": "john snow",
       "full_name": "John Snow",
       "email": "johnsnow@example.com",
       "hashed_password": "fakehashedsecret",  # 哈希加密
       "disabled": False,  # 是否活跃
   },
   "alice": {
       "username": "alice",
       "full_name": "Alice Wonderson",
       "email": "alice@example.com",
       "hashed_password": "fakehashedsecret2",
       "disabled": True,
   },
}


def fake_hash_password(password: str):
   return "fakehashed" + password


class User(BaseModel):
   username: str
   email: Optional[EmailStr] = None
   full_name: Optional[str] = None
   disabled: Optional[bool] = None


class UserInDB(User):
   hashed_password: str


def get_user(db, username: str):
   if username in db:
       user_dict = db[username]
       return UserInDB(**user_dict)


def fake_decode_token(token: str):
   user = get_user(fake_users_db, token)
   return user


async def get_current_user(token: str = Depends(oauth2_schema)):
   user = fake_decode_token(token)
   if not user:
       raise HTTPException(
           status_code=status.HTTP_401_UNAUTHORIZED,
           detail="Invalid authentication credentials",
           # 规范,如果认证失败 请求头返回下述信息
           headers={"WWW-Authenticate": "Bearer"},
       )
   return user


async def get_current_active_user(current_user: User = Depends(get_current_user)):
   if current_user.disabled:
       raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Inactive User")
   return current_user


@app04.post("/token")
async def login(form_data:OAuth2PasswordRequestForm= Depends()):
   user_dict = fake_users_db.get(form_data.username)
   if user_dict:
       user = UserInDB(**user_dict)
       hashed_password = fake_hash_password(form_data.password)
       if not hashed_password == user.hashed_password:
           raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,detail="Incorrect Username or password ")
       return {"access_token":user.username,"token_type":"bearer"}
   else:
       raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,detail="Incorrect Username or password ")


@app04.get("users/me")
async def read_user_self(current_user:User = Depends(get_current_active_user)):
   return current_user

2. 开发基于JWT的认证(Json Web Token)

内容太多 见GitHub

7. FastAPI中配置SQLALCHEMY

首先一个项目中需要包含如下文件

  • static 项目中的静态文件
  • templates 项目中被渲染的HTML
  • database 配置fastapi中的数据库
  • models 建立 模型类
  • schemas 响应的数据体规范 继承BaseModel
  • crud 对数据库的操作 如何获取信息等等
  • main.py  主应用 接口入口

"""
附上三个SQLAlchemy教程

SQLAlchemy的基本操作大全
http://www.taodudu.cc/news/show-175725.html

Python3+SQLAlchemy+Sqlite3实现ORM教程
https://www.cnblogs.com/jiangxiaobo/p/12350561.html

SQLAlchemy基础知识 Autoflush和Autocommit
https://zhuanlan.zhihu.com/p/48994990
"""

css https://semantic-ui.com/

8. 中间件

  • 中间件
  • 跨域资源共享(CORS)
  • 后台任务
  • 测试用例

中间件


request 经过中间件 路过应用 返回响应

中间件开发——给所有的响应加上响应时间

@app.middleware('http')
async def add_process_time_header(request: Request, call_next):  # call_next将接收request请求做为参数
    start_time = time.time()
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers['X-Process-Time'] = str(process_time)  # 添加自定义的以“X-”开头的请求头
    return response

注:带yield的依赖的退出部分的代码 和 后台任务 会在中间件之后运行

2. 跨域资源共享

代码实现

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "http://127.0.0.1",
        "http://127.0.0.1:8080"
    ],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

3. 实现celery的后台任务

def bg_task(framework: str):
    with open("README.md", mode="a") as f:
        f.write(f"## {framework} 框架精讲")


@app06.post("/background_tasks")
async def run_bg_task(framework: str, background_tasks: BackgroundTasks):
    """
    :param framework: 被调用的后台任务函数的参数
    :param background_tasks: FastAPI.BackgroundTasks
    :return:
    """
    background_tasks.add_task(bg_task, framework)
    return {"message": "任务已在后台运行"}


def continue_write_readme(background_tasks: BackgroundTasks, q: Optional[str] = None):
    if q:
        background_tasks.add_task(bg_task, "\n> 整体的介绍 FastAPI,快速上手开发,结合 API 交互文档逐个讲解核心模块的使用\n")
    return q


@app06.post("/dependency/background_tasks")
async def dependency_run_bg_task(q: str = Depends(continue_write_readme)):
    if q:
        return {"message": "README.md更新成功"}

4. https://coronavirus-tracker-api.herokuapp.com/#/

5. 编写 测试用例

"""Testing 测试用例"""
from fastapi.testclient import TestClient

from main import app
client = TestClient(app)  # 先pip install pytest


def test_run_bg_task():  # 函数名用“test_”开头是 pytest 的规范。注意不是async def
    response = client.post(url="/test6/background_tasks?framework=FastAPI")
    assert response.status_code == 200
    assert response.json() == {"message": "任务已在后台运行"}


def test_dependency_run_bg_task():
    response = client.post(url="/test6/dependency/background_tasks")
    assert response.status_code == 200
    assert response.json() is None


def test_dependency_run_bg_task_q():
    response = client.post(url="/test6/dependency/background_tasks?q=1")
    assert response.status_code == 200
    assert response.json() == {"message": "README.md更新成功"}

直接在终端输入 pytest
如果通过就

posted @ 2022-10-23 20:24  始識  阅读(222)  评论(0编辑  收藏  举报