欢迎来到啾啾的博客🐱。
记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。
有很多很多不足的地方,欢迎评论交流,感谢您的阅读和评论😄。
引言
本篇是一个Java程序员的FastAPI快速入门。
资料:https://fastapi.tiangolo.com/tutorial/bigger-applications/
1 服务器Uvicorn
Python的FastApi默认使用的内嵌服务器是Uvicorn,支持并发,线程管理交于外部组件。
Uvicorn是纯异步I/O,适合Python生态的异步架构,。
不同于Tomcat的NIO与半阻塞的线程管理,也不同于Netty通过Reactor模式实现全链路非阻塞的多路复用。
特性 | Uvicorn | Tomcat NIO | Netty |
---|---|---|---|
网络模型 | 异步 I/O(Asynchronous) | 多路复用 NIO(Non-blocking) | 多路复用(Reactor 模式) |
I/O 处理方式 | 全程非阻塞(事件循环 + 协程) | 连接监听非阻塞,请求处理阻塞 | 全程非阻塞(EventLoop 线程) |
线程模型 | 单线程事件循环 + 协程池 | Acceptor/Poller + 阻塞线程池 | 主从线程组(Boss/Worker) |
协议支持 | HTTP/1.1、WebSocket、HTTP/2 | HTTP/1.1、HTTP/2(有限) | 自定义协议(HTTP/WebSocket等) |
适用场景 | 高并发 API、实时通信 | 传统 Web 应用、Servlet 容器 | 高性能网络中间件、实时系统 |
2 接口
使用装饰器声明接口,比如:@app.get()
、@app.post()
3 Pydantic
FastAPI基于Pydantic来构建SpringBoot中常见的model,如xxRequest。但Pydantic模型功能更多,相当于集校验@Valid、文档@API等于一身。
FastAPI 是基于 Starlette (Web 部分) 和 Pydantic (数据验证部分) 构建的。
其中,Pydantic 是一款用于 Python 数据验证和设置管理 的库。
简单来说,它的核心作用就是:
- 把普通的数据(比如字典、JSON)变成结构化的数据对象。
- 在转换过程中,严格检查数据是否符合你定义的规则。
Pydantic的核心是BaseModel。通过继承它来定义自己的数据模型,这个模型就是告诉Pydantic开发者期望的数据结构。
from pydantic import BaseModel, EmailStr
from typing import Optional
class User(BaseModel):
name: str
age: int
email: Optional[EmailStr] = None
上面这个 User
类就定义了一个模型:
- 它必须有
name
字段,且类型是str
。 - 它必须有
age
字段,且类型是int
。 - 它有一个可选的
email
字段,类型是EmailStr
(Pydantic 内置的邮箱验证类型),默认值为None
。
Pydantic会自动校验模型内容:
data = {
"name": "张三",
"age": "25", # 注意这里 age 是字符串
"email": "zhangsan@example.com"
}
try:
user = User(**data)
print(user.name) # 输出: 张三
print(user.age) # 输出: 25 (Pydantic 自动转换为整数)
print(user.email) # 输出: zhangsan@example.com
print(user) # 输出: name='张三' age=25 email='zhangsan@example.com'
except Exception as e:
print(e)
在这个例子中,Pydantic 做了以下几件事:
- 它检查了所有字段。
- 它发现
age
是字符串"25"
,但模型要求是int
,于是它 自动完成了类型转换。 - 它甚至检查了
email
的格式,确保它是一个有效的邮箱地址。
另外,在FastAPI中,当请求体不符合Pydantic模型时,FastAPI会自动返回 422 Unprocessable Entity 错误响应,而无需手动 try...except
。
4 FastAPI的“依赖注入”
FastAPI的依赖注入允许声明你的路由操作(也就是那些用 @app.get()、@app.post() 等装饰器定义的函数)或者其他依赖所需要的“组件”或“服务”。FastAPI 会自动帮你把这些依赖“注入”到你的函数中。
FastAPI 的依赖注入系统基于可调用对象(函数或类)。你定义一个函数,让它返回你需要的依赖对象。然后,在你的路径操作函数中,使用 fastapi.Depends()
来声明你需要这个依赖。
比如认证依赖:
from fastapi import FastAPI, Depends, HTTPException, status
from typing import Annotated # Python 3.9+ 推荐使用 Annotated
app = FastAPI()
# 这是一个模拟的用户数据库
fake_users_db = {
"john_doe": {
"username": "john_doe",
"email": "john@example.com",
"hashed_password": "fakehashedpassword" # 实际应用中会是加密后的密码
}
}
# --- 定义一个简单的认证依赖 ---
# 这个函数会从请求头中获取一个 token,并验证它
async def get_current_user_from_token(token: str):
# 假设你的 token 就是用户名,这里只是一个简化示例
# 实际应用中,你会验证 JWT、API Key 或者其他认证凭证
if token not in fake_users_db:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无效的认证凭据",
headers={"WWW-Authenticate": "Bearer"}, # 告诉客户端需要 Bearer Token
)
user_data = fake_users_db[token]
return user_data
# --- 定义另一个更高级的依赖:获取当前活跃用户 ---
# 这个依赖依赖于上面的 get_current_user_from_token
async def get_current_active_user(
current_user: Annotated[dict, Depends(get_current_user_from_token)] # 这里使用了 Depends 来依赖上一个函数
):
# 假设我们有一个用户状态,比如 is_active
if not current_user.get("is_active", True): # 默认用户是活跃的
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="非活跃用户")
return current_user
# --- 路由:需要认证才能访问的用户信息 ---
@app.get("/users/me/")
async def read_users_me(
current_user: Annotated[dict, Depends(get_current_active_user)] # 在这里注入依赖
):
"""
获取当前登录用户的信息。
需要提供有效的认证令牌。
"""
return {"message": "欢迎回来!", "user": current_user}
# --- 路由:一个不需要认证的公开接口 ---
@app.get("/")
async def read_root():
"""
这是一个公开接口,不需要任何认证。
"""
return {"message": "Hello, World!"}
# 如何运行:
# 1. 保存代码为 main.py
# 2. 在命令行运行:uvicorn main:app --reload
# 3. 访问:
# - http://127.0.0.1:8000/
# - http://127.0.0.1:8000/users/me/ (不带token会401)
# - 带token访问:使用 Postman 或 curl,在请求头中添加 "token": "john_doe"
# curl -X 'GET' 'http://127.0.0.1:8000/users/me/' -H 'accept: application/json' -H 'token: john_doe'
在这个例子中,Depends的使用接近于Spring AOP的Before的编织效果。
依赖注入不仅限于认证,还可以注入数据库连接、配置对象、工具函数等任何你需要在路径操作中使用的“服务”。
5 依赖管理
pip与requirements.txt可以实现简单的依赖管理,其快捷、方便。
pip freeze > requirements.txt
但是对于复杂项目来说,存在不能辨别冲突依赖、区分依赖组、精确锁定依赖的问题。
可以使用Poetry或者PDM进行统一的依赖管理。