Skip to content

Commit 8b09321

Browse files
committed
post: fastapi middleware
1 parent 217afe1 commit 8b09321

File tree

34 files changed

+1044
-154
lines changed

34 files changed

+1044
-154
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
+++
2+
date = '2025-10-09T8:00:00+08:00'
3+
draft = false
4+
title = 'Fastapi Middleware'
5+
tags = ['Fastapi']
6+
+++
7+
8+
## Middleware
9+
10+
你可以添加中间件到 FastAPI 应用中。
11+
12+
“中间件” 是一个函数,它在每个请求被特定路径操作之前对其进行处理,同时在每个响应返回之前也对其进行处理。
13+
14+
- 在到达应用程序之前处理请求
15+
- 可以在请求中做一些事情,或运行任何需要的代码
16+
- 将处理后的请求传递给应用程序
17+
- 之后处理应用程序返回的响应
18+
- 可以对响应做一些事情,或运行任何需要的代码
19+
- 然后返回响应
20+
21+
### Create a Middleware 创建一个中间件
22+
23+
想要创建一个中间件,你可以在函数上面使用装饰器 `@app.middleware("http")`,该函数接受:
24+
25+
- `request` 请求
26+
- 一个函数 `call_next` 并将会接收 `request` 作为一个参数
27+
- 该函数会将 `request` 传递给对应的路径操作
28+
- 然后返回对应路由操作生成的 `response`
29+
- 你可以修改或者直接返回 `response`
30+
31+
```Python
32+
import time
33+
from fastapi import FastAPI, Request
34+
35+
app = FastAPI()
36+
37+
@app.middleware("http")
38+
async def add_process_time_header(request: Request, call_next):
39+
...
40+
```
41+
42+
> TIP
43+
44+
自定义专属 headers 可以使用 [X-prefix](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) 来添加。
45+
46+
但如果你有一个自定义的 header 并想要客户端能够看到这些信息,你需要使用 [Starlette's CORS docs](https://www.starlette.dev/middleware/#corsmiddleware) 中的参数参数 `expose_headers` 将其加入你的 CORS 设置里 ([CORS (Corss-Origin Resource Sharing)](https://fastapi.tiangolo.com/tutorial/cors/))。
47+
48+
#### Before and after the `response` 在响应前后
49+
50+
你也可以在 `request` 前后运行代码,也可以在 `response` 前后运行代码。
51+
52+
例如,你可以添加一个自定义标头 `X-Process-Time` 包含处理请求和返回响应的时间。
53+
54+
```Python
55+
import time
56+
from fastapi import Request, FastAPI
57+
58+
app = FastAPI
59+
60+
async def add_process_time_header(request: Request, call_next):
61+
start_time = time.perf_counter()
62+
response = await call_next(request)
63+
process_time = time.perf_counter() - start_time
64+
response.headers["X-Process-Time"] = str(process_time)
65+
return response
66+
```
67+
68+
这里使用 [time.perf_counter()](https://docs.python.org/3/library/time.html#time.perf_counter) 而不是 `time.time()` 因为它可以完成更加精细的控制。
69+
70+
### Multiple middleware execution order 多中间件执行顺序
71+
72+
无论当你使用 `@app.middleware()``@app.middleware()` 装饰器方法的时候,每个中间件都会将应用包装起来,形成一个栈。
73+
最后添加的中间件是 _outermost_ 最外层,第一个添加的是 _innermost_ 最内层。
74+
75+
在请求路径上,最外层的中间件首先运行,在响应路径上,最后运行。
76+
77+
例如:
78+
79+
```Python
80+
app.add_middleware(MiddlewareA)
81+
app.add_middleware(MiddlewareB)
82+
```
83+
84+
这会导致下面的执行顺序:
85+
86+
- **Request**: MiddlewareB -> MiddlewareA -> route
87+
- **Response**: route -> MiddlewareA -> MiddlewareB
88+
89+
这种栈的行为保证了中间件执行是一个可预测与可控制的顺序。
90+
91+
### Other middlewares 其他中间件
92+
93+
[Advanced User Guide: Advanced Middleware](https://fastapi.tiangolo.com/advanced/middleware/) 中有其他中间件的描述。
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
+++
2+
date = '2025-10-10T8:00:00+08:00'
3+
draft = false
4+
title = 'Fastapi Background Tasks'
5+
tags = ['Fastapi']
6+
+++
7+
8+
## Background Tasks 后台任务
9+
10+
你可以定义一个在返回响应之后运行的后台任务。
11+
这对请求之后执行一些操作十分有用,客户端无需一直等待操作任务完成再接收响应。
12+
13+
这包含一些例子:
14+
15+
- 执行操作后发送电子邮件
16+
- 由于连接邮件服务器并发送邮件一般会比较“慢”(几秒钟),你可以立刻返回响应并在后台发送邮件请求。
17+
18+
- 处理数据
19+
- 例如,你收到了一个文件需要缓慢处理,你可以返回一个 "Accepted" 响应 (HTTP 202) 并在后台处理文件。
20+
21+
### Using `Background Tasks` 使用后台任务
22+
23+
首先要导入 `BackgroundTasks` 并在执行函数中定义一个路径参数,使用 `BackgroundTasks` 类型声明。
24+
25+
```Python
26+
from fastapi import BackgroundTasks, FastAPI
27+
28+
app = FastAPI()
29+
30+
def write_notification(email: str, message=""):
31+
with open("log.txt", mode="w") as email_file:
32+
content = f"notification for {email}: {message}"
33+
email_file.write(content)
34+
35+
@app.post("/send-notification/{email}")
36+
async def send_notification(email: str, backgroud_tasks: Background(Tasks): # Add parameter here
37+
background_tasks.add_task(write_notification, email, message="some notification") # add backgroud task here
38+
return {"message": "Notification sent in the background"}
39+
```
40+
41+
### Create a task function 创建任务函数
42+
43+
创建一个函数放到后台运行,只是一个接收参数的基本函数,可以是 `async def` 或者就普通的 `def` 函数,FastAPI 会正确的处理它。
44+
45+
在这个例子中的任务函数将会编写文件,并且写入操作不使用 `async``await` 故使用 `def` 定义了一个基本的函数。
46+
47+
```Python
48+
# 任务函数
49+
def write_notification(email: str, message=""):
50+
with open("log.txt", mode="w") as email_file:
51+
content = f'notification for {email}: {message}'
52+
email_file.write(content)
53+
```
54+
55+
### Add the background task 添加后台任务
56+
57+
在路径操作函数内部,使用 `BackgroundTasks` 对象方法 `.add_task()` 将任务函数添加到后台任务中。
58+
59+
```Python
60+
@app.post("/send-notification/{email}")
61+
async def send_notification(email: str, background_tasks: BackgroundTasks):
62+
background_tasks.add_task(write_notification, email, message="some notification")
63+
return {"message": "Notification sent in the background"}
64+
```
65+
66+
`.add_task()` 接受下面参数:
67+
68+
- 一个后台运行的任务函数 (`write_notification`)
69+
- 一系列参数,并将按顺序传入任务函数中 (`email`)
70+
- 任何通过关键字参数传入任务函数中 (`message=some notification`)
71+
72+
### Dependency Injection 依赖注入
73+
74+
使用 `BackgroundTasks` 也同样适用于依赖注入系统,你可以在多层级声明一系列的 `BackgroundTasks`:如路径操作函数,依赖,子依赖等。
75+
76+
FastAPI 知道每种情况下应该怎么做,以及如何重用一个对象,以便所有的后台对象被合并到一起,并之后在后台运行。
77+
78+
```Python
79+
from typing import Annotated
80+
from fastapi import BackgroundTasks, Depends, FastAPI
81+
82+
app = FastAPI()
83+
84+
# 日志写入函数
85+
def write_log(message: str):
86+
with open("log.txt", mode="a") as log:
87+
log.write(message)
88+
89+
# 依赖函数
90+
def get_query(background_tasks: BackgroundTasks, q: str | None = None):
91+
if q:
92+
message = f"found query: {q}\n"
93+
background_tasks.add_task(write_log, message)
94+
return q
95+
96+
@app.post("/send-notification/{email}")
97+
async def send_notification(
98+
email: str,
99+
background_tasks: BackgroundTasks,
100+
q: Annotated[str, Depends(get_query)]
101+
):
102+
message = f"message to {email}\n"
103+
background_tasks.add_task(write_log, message)
104+
return {"message": "Message sent"}
105+
```
106+
107+
上面代码中 `get_query()` 的参数 `q` 为一个 query 查询参数,从 url 中 `?q=xxx` 获取,然后再通过后台任务将其写入日志。
108+
109+
### Technical Details 技术细节
110+
111+
类型 `BackgroundTasks` 直接源于 [starlette.background](https://www.starlette.dev/background/)。
112+
该类可以直接从 FastAPI 导入,这样避免了从 `starlette.background` 导入 `BackgroundTask` (不含s)。
113+
114+
### Caveat 警告
115+
116+
如果需要大量的后台运算,且并不一定需要让他们在相同的进程中运行(例如无需共享内存),那么可以考虑使用 [Celery](https://docs.celeryq.dev/) 这样更大的工具。
117+
118+
这往往需要更加复杂的配置,例如一个任务队列管理器,想 RabbitMQ 或 Redis,但他们允许你在多个进程甚至多个服务器中运行后台任务。
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
+++
2+
date = '2025-10-23T8:00:00+08:00'
3+
draft = true
4+
title = 'Fastapi Straming Response'
5+
tags = ['Fastapi']
6+
+++

content/posts/2025-10-23_morden-js-code-quailty.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
+++
22
date = '2025-10-23T8:00:00+08:00'
33
draft = false
4-
title = 'Code Quailty'
4+
title = 'Morden Javascript Tutorial Chapter 3.1 - Code Quailty'
55
tags = ['Javascript']
66
+++
77

content/posts/2025-10-24_fastapi-lifespan-events.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ title = 'Fastapi Lifespan Events'
55
tags = ['Fastapi']
66
+++
77

8-
## Lifespan Events
8+
## Lifespan Events 生命周期事件
99

1010
通过生命周期事件可以定义在应用开启之前需要执行的代码,这意味着这些代码会在开始接收外部请求之前被执行一次。
1111
同样地,也可以定义应用在关闭的时候定义需要执行的代码,在尽力处理完所有请求后,该代码会被执行一次。
1212

1313
这对于设置需要在整个 app 的请求间共享的资源时非常有用,或者是需要进行清理工作的时候。
1414
例如,一个数据库连接池,或者加载一个共享的机器学习模型。
1515

16-
### Use Case
16+
### Use Case 使用示例
1717

1818
下面通过一个例子说明如何使用。
1919

@@ -23,7 +23,7 @@ tags = ['Fastapi']
2323

2424
这就是需要解决的问题,需要在请求响应之前加载模型,也不是在代码被加载的时候加载模型。
2525

26-
### Lifespan
26+
### Lifespan 生命周期
2727

2828
可以通过在 `FastAPI` app 中使用 `lifespan` 参数来定义启动和关闭逻辑,以及一个 "context manager" (上下文管理器)。
2929

@@ -61,7 +61,7 @@ async def predict(x: float):
6161
然后,在 `yield` 后面,卸载模型。
6262
这段改名将在完成请求之后执行,即关闭之前,这样会释放内存和 CPU 资源。
6363

64-
#### Lifespan function
64+
#### Lifespan function 生命周期函数
6565

6666
第一件注意到的事是,定义了一个带 `yield` 的 async function,这与带 `yield` 的 Dependencies 相同。
6767

@@ -74,7 +74,7 @@ async def lifespan(app: FastAPI):
7474

7575
`yield` 之前的部分会在应用开启之前执行,`yield` 之后的部分会在应用结束之后执行。
7676

77-
#### Async Context Manager
77+
#### Async Context Manager 异步上下文管理器
7878

7979
该函数使用 `@asynccontextmanager` 异步上下文管理器装饰,将函数转化成一个 "**async context manager**"。
8080

@@ -132,7 +132,7 @@ async def shotdown():
132132
print("关闭xxx")
133133
```
134134

135-
### Technical Details
135+
### Technical Details 技术细节
136136

137137
在 ASGI 协议规范下,这是 [Lifespan Protocol](https://asgi.readthedocs.io/en/latest/specs/lifespan.html) 的一部分,并且定义了 `startup``shutdown` 的事件。
138138

content/posts/2026-sse.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
+++
2+
date = '2025-10-30T8:00:00+08:00'
3+
draft = true
4+
title = 'SSE protocol'
5+
tags = ['Network']
6+
+++

0 commit comments

Comments
 (0)