(四)深入理解 ASGI 和 Uvicorn:FastAPI 的基础概念
在现代 Web 开发中,ASGI
和Uvicorn
是理解和使用 FastAPI 的关键组件。本文将深入解析 ASGI 和 Uvicorn,并简要介绍 FastAPI 的路由和请求参数处理,同时深入探讨中间件与依赖注入。
1. 什么是 ASGI?
ASGI(Asynchronous Server Gateway Interface)是一个为 Python 网络服务器和应用程序设计的异步标准接口。它是 WSGI(Web Server Gateway Interface)的异步版本,旨在支持 WebSockets、HTTP/2 以及其他需要异步通信的现代协议。
ASGI 的特点:
- 异步支持:允许处理异步请求,提高并发性能。
- 灵活性:支持 HTTP、WebSockets 等多种协议。
- 标准化:提供统一接口,方便不同框架和服务器之间的互操作性。
2. 什么是 Uvicorn?
Uvicorn 是一个基于 ASGI 的高性能 Web 服务器,用于运行 ASGI 应用(例如 FastAPI)。Uvicorn 使用 uvloop 和 httptools,以提供卓越的性能和快速的响应时间。
Uvicorn 的优势:
- 高性能:基于 uvloop 和 httptools,极大提升了处理速度。
- 轻量级:设计简洁,启动速度快,占用资源少。
- 易于使用:简单的命令行界面,方便开发和部署。
安装 Uvicorn
pip install "uvicorn[standard]"
运行 FastAPI 应用
uvicorn main:app --reload
在命令中,main
是你的 Python 文件名(不带.py
后缀),app
是 FastAPI 实例。--reload
标志用于在代码修改时自动重启服务器,非常适合开发环境。
3. FastAPI 的路由
FastAPI 使用装饰器来定义路径操作(Route)。路径操作是指 API 的具体端点,例如 GET、POST 请求。
定义简单的 GET 请求
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"message": "Hello, World!"}
在这个示例中,当访问根路径(/)时,将会返回一个包含 message 键的 JSON 响应。
定义带有路径参数的请求
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
在这个示例中,item_id
是一个路径参数,FastAPI 会自动将其转换为指定的类型(如 int),并传递给视图函数。
4. 请求参数和验证
FastAPI 支持多种类型的请求参数,包括路径参数、查询参数、请求体参数等,并通过 Pydantic 进行验证。
查询参数
@app.get("/items/")
async def read_item(name: str, age: int):
return {"name": name, "age": age}
在这个示例中,name
和age
是查询参数,访问 http://127.0.0.1:8000/items/?name=John&age=30 会返回{"name": "John", "age": 30}
。
请求体参数
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
is_offer: bool = None
@app.post("/items/")
async def create_item(item: Item):
return item
在这个示例中,Item
是一个Pydantic
模型,定义了请求体的结构和验证规则。当创建一个新的 Item 时,FastAPI 会自动验证请求体数据,并将其解析为 Item 实例。
5. 中间件
中间件是指在处理请求和响应之间插入的一段处理逻辑,通常用于执行诸如日志记录、认证、修改请求和响应等任务。
使用中间件
from fastapi import FastAPI, Request
import time
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
@app.middleware("http")
用于注册一个 HTTP 中间件函数,该函数会在每个请求处理之前和之后执行。具体来说,它在请求进入任何路径操作函数之前执行一些预处理逻辑,并且在路径操作函数处理完请求之后执行一些后处理逻辑。如果注册了多个 HTTP 中间件函数,它们的执行顺序是按注册的顺序执行。
在这个示例中,中间件会在每个请求处理前后记录处理时间,并将其添加到响应头中。
6. 依赖注入与 Depends 的使用
依赖注入(Dependency Injection)是一个设计模式,它使得对象之间的依赖关系通过外部注入而不是内部创建,从而提高代码的可维护性和可测试性。FastAPI 提供了强大的依赖注入系统,允许你在路径操作函数中声明依赖项,FastAPI 会自动解决这些依赖关系。
Depends
是实现依赖注入的核心工具。它允许你在路径操作函数中声明依赖项,FastAPI 会自动调用这些依赖项并将返回值传递给路径操作函数。
简单的依赖注入
from fastapi import Depends, FastAPI
app = FastAPI()
def common_parameters(q: str = None, skip: int = 0, limit: int = 10):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons
- 在这个示例中,
common_parameters
是一个依赖项,定义了一组常用参数。read_items
路由通过Depends
函数引入这些参数,从而实现参数复用。
依赖注入的高级用法
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import OAuth2PasswordBearer
from typing import Optional
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
async def get_current_user(token: str = Depends(oauth2_scheme)):
if token != "fake-token":
raise HTTPException(status_code=401, detail="Invalid token")
return {"username": "fake-user"}
@app.get("/users/me")
async def read_users_me(current_user: dict = Depends(get_current_user)):
return current_user
- 在这个示例中,
get_current_user
是一个依赖项,用于从请求中提取和验证OAuth2 token
。如果 token 无效,将抛出HTTPException
。read_users_me
路由通过Depends
函数引入get_current_user
依赖项,从而实现认证逻辑。
ASGI 和 Uvicorn 是 FastAPI 高性能和异步能力的基础。通过深入理解这两个组件,你可以更好地利用 FastAPI 构建高性能、高并发的 Web 应用。结合 FastAPI 的自动文档生成和数据验证功能,你能够快速开发和维护现代 Web 应用。中间件和依赖注入进一步增强了 FastAPI 的功能和灵活性,使得开发变得更加高效和可维护。
希望这篇文章能帮助你深入理解 ASGI 和 Uvicorn 的基础概念,为你的高级编程实践奠定坚实的基础。如果你有任何问题或需要进一步的帮助,请随时联系我。
转载自:https://juejin.cn/post/7379437165484523557