Skip to content

Commit 47fe242

Browse files
committed
post: fastapi app and request
1 parent e7d8974 commit 47fe242

File tree

17 files changed

+217
-16
lines changed

17 files changed

+217
-16
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
+++
2+
date = '2025-11-25T8:00:00+08:00'
3+
draft = true
4+
title = 'FastAPI app and request'
5+
tags = ['FastAPI']
6+
+++
7+
8+
在 FastAPI 中,`Request` 对象和 `FastAPI` 应用实例 (`app`) 是核心概念,它们在应用状态管理和依赖注入中扮演着关键角色。
9+
本文将介绍它们的关系、设计理念,以及如何利用 `app.state` 实现单例模式。
10+
11+
---
12+
13+
## FastAPI 对象
14+
15+
`FastAPI` 对象是整个应用的核心实例:
16+
17+
```python
18+
from fastapi import FastAPI
19+
20+
app = FastAPI(title="示例应用")
21+
```
22+
23+
### 核心职责
24+
25+
- **路由管理**:通过 `@app.get()``@app.post()` 等装饰器定义 URL 到视图函数的映射。
26+
- **中间件和事件管理**:可注册中间件处理请求/响应,支持 `startup``shutdown` 事件。
27+
- **应用状态管理**:提供 `app.state`,可存放全局单例对象、数据库连接池、配置等。
28+
- **异常处理与依赖注入**:管理异常处理器,并协助依赖注入机制。
29+
30+
### 单例模式存储
31+
32+
这里要使用 app 的 State 对象存储单例,app 中定义如下
33+
34+
```python
35+
# app
36+
self.state: Annotated[
37+
State,
38+
Doc(
39+
"""
40+
A state object for the application. This is the same object for the
41+
entire application, it doesn't change from request to request.
42+
43+
You normally wouldn't use this in FastAPI, for most of the cases you
44+
would instead use FastAPI dependencies.
45+
46+
This is simply inherited from Starlette.
47+
48+
Read more about it in the
49+
[Starlette docs for Applications](https://www.starlette.dev/applications/#storing-state-on-the-app-instance).
50+
"""
51+
),
52+
] = State()
53+
54+
```
55+
56+
State 源码如下,简单看就是一个字典
57+
58+
```Python
59+
# State
60+
class State:
61+
"""
62+
An object that can be used to store arbitrary state.
63+
64+
Used for `request.state` and `app.state`.
65+
"""
66+
67+
_state: dict[str, Any]
68+
69+
def __init__(self, state: dict[str, Any] | None = None):
70+
if state is None:
71+
state = {}
72+
super().__setattr__("_state", state)
73+
74+
def __setattr__(self, key: Any, value: Any) -> None:
75+
self._state[key] = value
76+
77+
def __getattr__(self, key: Any) -> Any:
78+
try:
79+
return self._state[key]
80+
except KeyError:
81+
message = "'{}' object has no attribute '{}'"
82+
raise AttributeError(message.format(self.__class__.__name__, key))
83+
84+
def __delattr__(self, key: Any) -> None:
85+
del self._state[key]
86+
87+
```
88+
89+
使用示例
90+
91+
```Python
92+
@asynccontextmanager
93+
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
94+
"""
95+
FastAPI 应用生命周期管理
96+
97+
启动时初始化资源,关闭时清理资源
98+
"""
99+
# 初始化单例
100+
print("[应用启动] 创建 Admin Agent 服务实例...")
101+
admin_agent_service = AdminService(mcp_server_configuration)
102+
app.state.admin_agent_service = admin_agent_service
103+
await admin_agent_service.initialize() # 立即初始化 Agent
104+
105+
yield # 应用运行其间
106+
107+
# 关闭数据库连接池
108+
await close_database()
109+
110+
# 关闭 Redis 连接
111+
await close_redis()
112+
```
113+
114+
当需要获取该单例的时候,通过依赖注入得到
115+
116+
```Python
117+
def get_admin_service(request: Request) -> AdminService:
118+
"""获取 Admin Agent 服务实例 (app.state)"""
119+
return request.app.state.admin_agent_service
120+
121+
@app.post()
122+
async def send_admin_message(request: Request) -> StreamingResponse:
123+
...
124+
125+
# 获取 Admin Agent 服务实例
126+
admin_service = get_admin_service(request)
127+
128+
...
129+
130+
# SSE
131+
return StreamingResponse(
132+
media_type="application/x-ndjson",
133+
headers={
134+
"Cache-Control": "no-cache",
135+
"Connection": "keep-alive",
136+
"X-Accel-Buffering": "no",
137+
}
138+
)
139+
```
140+
141+
- **作用**:确保整个应用中只有一个 `AdminService` 实例被创建和共享。
142+
- **优势**
143+
- 避免重复初始化资源
144+
- 统一管理全局服务
145+
- 可在请求中安全访问
146+
147+
## Request 对象
148+
149+
`Request` 对象封装了一次 HTTP 请求的上下文:
150+
151+
```python
152+
def get_admin_service(request: Request) -> AdminService:
153+
"""获取 Admin Agent 服务实例 (app.state)"""
154+
return request.app.state.admin_agent_service
155+
```
156+
157+
### 核心职责
158+
159+
- **封装请求信息**:包含方法、路径、headers、query 参数、body 等。
160+
- **提供应用访问入口**`request.app` 指向该请求所属的 `FastAPI` 实例。
161+
- **支持依赖注入**:可以作为依赖函数参数,让函数获取应用状态或服务实例。
162+
163+
`Request``app` 的关联
164+
165+
1. **解耦请求与全局变量**
166+
167+
请求处理函数无需直接引用全局 `app`,通过 `request.app` 即可访问应用实例和状态。
168+
169+
2. **支持多实例应用**
170+
171+
同一个 Python 进程可以启动多个 FastAPI 实例,每个实例的 `state` 独立。请求绑定到具体实例,`request.app` 指向对应实例。
172+
173+
3. **依赖注入友好**
174+
175+
在依赖函数中,通过 `Request` 获取 `app.state` 中的单例对象:
176+
177+
```python
178+
from fastapi import Depends
179+
180+
def get_admin_service(request: Request) -> AdminService:
181+
return request.app.state.admin_service
182+
183+
@app.get("/admin")
184+
async def admin_endpoint(service: AdminService = Depends(get_admin_service)):
185+
return {"message": service.hello()}
186+
```
187+
188+
- **工作流程**
189+
1. FastAPI 收到请求,创建 `Request` 对象。
190+
2. DI 系统调用 `get_admin_service`,将 `Request` 注入。
191+
3. 函数通过 `request.app.state.admin_service` 获取全局单例。
192+
4. 依赖对象传入 endpoint。
193+
194+
## 总结
195+
196+
- `app`:FastAPI 实例,负责路由、事件、中间件、依赖和状态管理。
197+
- `app.state`:存储单例对象和全局资源,实现单例模式。
198+
- `Request`:每次请求的封装,携带请求信息和对 `app` 的引用。
199+
- `request.app.state`:结合依赖注入,让每个请求安全访问全局单例对象,无需使用全局变量。
200+
201+
这种设计模式既保证了应用单例对象的统一管理,又支持依赖注入和多实例应用,非常适合现代 Web 应用开发。

0 commit comments

Comments
 (0)