FastAPI初识

371 阅读6分钟

FastAPI简介

FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架。

FastAPI官方文档:fastapi.tiangolo.com/zh/

FastAPI特点:

  1. 性能优越

    与Django、Flask不同,FastAPI属于异步框架。在编写函数时加上async,就可以实现异步响应功能。python对于异步有较好的支持是从python3.6开始的,所以使用FastAPI框架应该使用python3.6及以上的版本。

  2. 开发效率高

    提高功能开发速度约 200% 至 300%,官方数据。

  3. 减少40%人为bug

    业务开发方面产生的bug,由于他的明确的错误提示、标准的开发准则等。

  4. 易学易用

    官方文档友好、易懂、详细。

  5. 精简编码,代码重复率低。

  6. 自带API交互文档,开发成果随时交付。

    /docs/redoc

  7. API开发标准化

    适合RESTful API风格开发。

FastAPI 相关库

1.Starlette、Pydantic库:

python3.5以上类型提示 type hints :

Python 是动态类型语言,运行时不需要指定变量类型。这一点是不变的,但是2015年9月Python 3.5 引入了一个类型系统,允许开发者指定变量类型。它的主要作用是方便开发,供IDE 和各种开发工具使用,对代码运行不产生影响,运行时会过滤类型信息。

def get_user_info(name: str, age: int, status: bool):
    print(name, age, status)


get_user_info(name="林一峰", age=[], status="wang")

Pydantic 是一个基于python类型提示来定义验证,序列化和文档库,FastAPI用它来做数据模型校验。

Starlette 是一种轻量级的ASGI库,特别适合用来构建高性能的 asyncio 服务。

  1. fastapi、uvicorn库
  • fastapi FastAPI核心库,python web框架

  • uvicorn 可安装的ASGI框架,用于加载和提供应用程序的服务器

最简单的开始

安装:

pip install fastapi
pip install uvicorn

最简单的应用:

# step1: 导入核心库
from fastapi import FastAPI 
import uvicorn 

app = FastAPI()  # step2: 创建一个 FastAPI 实例


@app.get("/") # step3: 创建一个路径操作,定义一个路径操作装饰器
async def home(): # step4: 定义路径操作函数
    return {"message": "welcome!"} # step5: 返回内容


if __name__ == '__main__':
    # step6: 运行开发服务器
    uvicorn.run(app="main:app", host='127.0.0.1', port=8082, reload=True)

自带API交互文档:

通过访问http://127.0.0.1:8082/docs免费获得 Swagger UI

通过访问 http://127.0.0.1:8082/redoc 免费获得 ReDoc

路径参数

  1. 路径参数

    @app.get("/items/{item_id}")
    async def read_item(item_id):
        return {"item_id": item_id}
    

    路径参数 item_id 的值将作为参数 item_id 传递给你的函数,访问http://127.0.0.1:8082/items/1

  2. 有类型的路径参数

    @app.get("/items/{item_id}")
    async def read_item(item_id: int): # int型
        return {"item_id": item_id}
    

    item_id 被声明为 int 类型

  3. 数据转换

    访问 http://127.0.0.1:8000/items/3,FastAPI通过上面的类型声明提供了对请求的自动解析,函数接收到的不是字符串3,而是int型的3。

  4. 数据校验

    访问http://127.0.0.1:8000/items/foo,会返回明确的错误提示。

查询参数

  1. 查询参数

    声明不属于路径参数的其他函数参数时,它们将被自动解释为"查询字符串"参数。

    fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
    
    @app.get("/items") 
    async def read_item(skip: int = 0, limit: int = 10): 
        return fake_items_db[skip : skip + limit]
    

    查询字符串是键值对的集合,这些键值对位于 URL 的  之后,并以 & 符号分隔

    访问:http://127.0.0.1:8000/items/?skip=0&limit=10,查询参数为skiplimit

  2. 可选参数

    默认值设置为 None 来声明可选查询参数。

    from typing import Union
    
    @app.get("/items/{item_id}") 
    async def read_item(item_id: str, q: Union[str, None] = None): 
        if q: 
            return {"item_id": item_id, "q": q} 
        return {"item_id": item_id}
    

    函数参数 q 将是可选的,并且默认值为 None

  3. 查询参数类型转换

    from typing import Union
    
    async def read_item(item_id: str, q: Union[str, None] = None, short: bool = False): 
        item = {"item_id": item_id} 
        if q: 
            item.update({"q": q}) '
        if not short: 
            item.update( {"description": "This is an amazing item that has a long description"} ) 
        return item
    
    

    以下几种方式任何其他的变体形式(大写,首字母大写等等),你的函数接收的 short 参数都会是布尔值 True,它们将被自动转换为布尔类型。

    http://127.0.0.1:8000/items/foo?short=1
    http://127.0.0.1:8000/items/foo?short=True
    http://127.0.0.1:8000/items/foo?short=true
    http://127.0.0.1:8000/items/foo?short=on
    http://127.0.0.1:8000/items/foo?short=yes
    
  4. 必需查询参数

    如果你想使该参数成为可选的,则将默认值设置为 None。但当你想让一个查询参数成为必需的,不声明任何默认值就可以。

请求体

  1. 请求体

    当你需要将数据从客户端(例如浏览器)发送给 API 时,你将其作为「请求体」发送。

    我们使用Pydantic模型来声明请求体。

    不能使用 GET 操作(HTTP 方法)发送请求体,要发送数据,你必须使用下列方法之一:POST(较常见)、PUTDELETE 或 PATCH

    from typing import Union 
    from fastapi import FastAPI 
    from pydantic import BaseModel # step1: 导入Pydantic的BaseModel
    
    # step2: 创建数据模型
    class Goods(BaseModel): 
        name: str 
        description: Union[str, None] = None 
        price: float 
        tax: Union[float, None] = None 
    
    app = FastAPI() 
    
    @app.post("/goods/")  
    async def create_goods(goods: Goods): # step3: 声明为参数
        return goods
    

    传入参数:

    {
        "name": "Foo",
        "description": "An optional description",
        "price": 45.2,
        "tax": 3.5
    }
    

    或:

    { 
        "name": "Foo", 
        "price": 45.2 
    }
    
  2. 请求体 + 路径参数 + 查询参数

    还可以同时声明请求体、路径参数和查询参数。FastAPI会识别它们中的每一个,并从正确的位置获取数据。

    class Goods(BaseModel): 
        name: str 
        description: Union[str, None] = None 
        price: float 
        tax: Union[float, None] = None 
    
    app = FastAPI() 
    
    @app.post("/goods/info/{goods_id}")  
    async def create_goods_info(goods_id:int,goods: Goods,q:Union[str,None]=None): 
    
        result = {"goods_id": goods_id, **goods.dict()}
        if q: 
            result.update({"q": q})
        return result
    

    函数参数将依次按如下规则进行识别:

    • 如果在路径中也声明了该参数,它将被用作路径参数。
    • 如果参数属于单一类型(比如 intfloatstrbool 等)它将被解释为查询参数。
    • 如果参数的类型被声明为一个 Pydantic 模型,它将被解释为请求体。

查询参数和字符串校验

  1. 额外的校验

    添加约束条件:即使 q 是可选的,但只要提供了该参数,则该参数值不能超过50个字符的长度。

    from typing import Union
    from fastapi import FastAPI, Query # step1: 导入Query
    
    app = FastAPI() 
    @app.get("/items/") 
    async def read_items(q: Union[str, None] = Query(default=None, max_length=50)):  
    # 将 Query 用作查询参数的默认值,并将它的 max_length 参数设置为 50
        results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} 
        if q: 
            results.update({"q": q}) 
        return results
    
    

路径参数和数值校验

与使用 Query 为查询参数声明更多的校验和元数据的方式相同,你也可以使用 Path 为路径参数声明相同类型的校验和元数据。

from fastapi import FastAPI, Path # 导入Path

app = FastAPI() 

@app.get("/items/{item_id}") 
async def read_items(item_id: int = Path(title="The ID of the item to get", ge=1) ): 
# 添加 ge=1 后,item_id 将必须是一个大于(greater than)或等于(equal)1 的整数
    results = {"item_id": item_id} 
    return results

同样的规则适用于:

  • gt: 大于(greater than)
  • le: 小于等于(less than or equal)

QueryPath 以及你后面会看到的其他类继承自一个共同的 Param 类,它们都共享相同的所有你已看到并用于添加额外校验和元数据的参数。

响应模型

可以在任意的路径操作中使用 response_model 参数来声明用于响应的模型。

from typing import Any, Union 
from fastapi import FastAPI 
from pydantic import BaseModel

app = FastAPI() 

class UserIn(BaseModel): 
    username: str 
    password: str 
    full_name: Union[str, None] = None 
    
class UserOut(BaseModel): 
    username: str 
    full_name: Union[str, None] = None 

@app.post("/user/", response_model=UserOut) 
async def create_user(user: UserIn) -> Any: 
    return user

一点实战