在 FastAPI 中,exception_handler
和 middleware
的执行顺序是一个常见的问题。理解它们的执行顺序对于正确处理请求和异常非常重要。
1.源码分析
查看starlette.applications.Starlette类的构造方法,可以看到异常处理器和中间件的类型: 查看源码中添加中间件、异常处理器、路由的逻辑:
查看源码中构建应用的中间件栈的方法:
这个函数 build_middleware_stack
是一个用于构建 ASGI(Asynchronous Server Gateway Interface)应用的中间件栈的方法。ASGI 是一种异步 Web 服务器与异步 Web 应用或框架之间的接口标准。中间件是位于应用和服务器之间的组件,可以处理请求和响应,通常用于日志记录、身份验证、错误处理等。
逐步解释
-
定义函数和初始化变量:
pythondef build_middleware_stack(self) -> ASGIApp: debug = self.debug error_handler = None exception_handlers: dict[typing.Any, typing.Callable[[Request, Exception], Response]] = {}
self.debug
是一个布尔值,表示是否开启调试模式。error_handler
用于存储处理 500 错误或通用异常的处理器。exception_handlers
是一个字典,用于存储特定异常类型的处理器。
-
遍历异常处理器:
pythonfor key, value in self.exception_handlers.items(): if key in (500, Exception): error_handler = value else: exception_handlers[key] = value
- 遍历
self.exception_handlers
字典中的每个键值对。 - 如果键是
500
或Exception
,则将对应的值赋给error_handler
。 - 否则,将键值对添加到
exception_handlers
字典中。
- 遍历
-
构建中间件列表:
pythonmiddleware = ( [Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug)] + self.user_middleware + [Middleware(ExceptionMiddleware, handlers=exception_handlers, debug=debug)] )
- 创建一个中间件列表
middleware
。 - 首先添加一个
ServerErrorMiddleware
实例,用于处理服务器错误,传递error_handler
和debug
参数。 - 然后添加用户自定义的中间件
self.user_middleware
。 - 最后添加一个
ExceptionMiddleware
实例,用于处理其他异常,传递exception_handlers
和debug
参数。
- 创建一个中间件列表
-
构建中间件栈:
pythonapp = self.router for cls, args, kwargs in reversed(middleware): app = cls(app=app, *args, **kwargs)
- 初始化
app
为self.router
,这是应用的路由处理程序。 - 反向遍历
middleware
列表,确保中间件按照正确的顺序被应用。 - 对于每个中间件类
cls
,使用app
作为参数创建一个新的中间件实例,并将结果赋值回app
。
- 初始化
-
返回最终的应用:
pythonreturn app
- 返回最终构建好的 ASGI 应用
app
。
- 返回最终构建好的 ASGI 应用
总结
这个函数的主要作用是构建一个中间件栈,其中包含处理服务器错误和特定异常的中间件,以及用户自定义的中间件。通过反向遍历中间件列表,确保中间件按照正确的顺序被应用到应用的路由处理程序上。最终返回一个完整的 ASGI 应用。
执行顺序
-
Middleware:
- Middleware 是在请求到达路由处理函数之前和响应返回客户端之后执行的。
- Middleware 按照它们在应用中注册的顺序从上到下依次执行。
- 在请求阶段,Middleware 会按顺序依次执行。
- 在响应阶段,Middleware 会按逆序执行(即最后一个注册的 Middleware 最先执行)。
-
Exception Handler:
- Exception Handler 是在处理请求过程中抛出异常时执行的。
- 如果在 Middleware 或路由处理函数中抛出了异常,并且该异常没有被其他代码捕获,那么 FastAPI 会查找注册的异常处理器来处理这个异常。
- Exception Handler 的执行优先级高于 Middleware 的响应处理部分。
具体流程
-
请求阶段:
- 请求首先经过 Middleware。
- 然后请求被传递给路由处理函数。
- 如果在 Middleware 或路由处理函数中抛出了异常,FastAPI 会查找并调用相应的 Exception Handler。
-
响应阶段:
- 路由处理函数生成响应。
- 响应经过 Exception Handler(如果有的话)。
- 响应再经过 Middleware 的响应处理部分,按逆序执行。
示例
假设你有以下 FastAPI 应用:
python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.middleware import Middleware
app = FastAPI()
# Middleware 1
@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
# Middleware 2
@app.middleware("http")
async def add_custom_header(request: Request, call_next):
response = await call_next(request)
response.headers["X-Custom-Header"] = "CustomValue"
return response
# Exception Handler
@app.exception_handler(ValueError)
async def value_error_exception_handler(request: Request, exc: ValueError):
return JSONResponse(
status_code=400,
content={"message": str(exc)},
)
# Route
@app.get("/")
async def read_root():
raise ValueError("This is a value error")
return {"Hello": "World"}
执行顺序分析
-
请求阶段:
- 请求首先经过
add_custom_header
Middleware。 - 然后经过
add_process_time_header
Middleware。 - 最后到达路由处理函数
read_root
。
- 请求首先经过
-
异常处理:
- 在
read_root
中抛出ValueError
。 - FastAPI 查找并调用
value_error_exception_handler
,生成一个 400 错误响应。
- 在
-
响应阶段:
- 响应首先经过
add_process_time_header
Middleware。 - 然后经过
add_custom_header
Middleware。 - 最后返回给客户端。
- 响应首先经过
通过以上分析,你可以看到 Middleware 和 Exception Handler 的执行顺序及其在请求和响应处理中的角色。希望这能帮助你更好地理解和使用 FastAPI。