FastAPI的中间件本质上是一个函数,它在每个请求被特定的路径操作函数处理之前和之后工作,通过中间件,你可以在请求被处理之前或者之后做一些事情。
下面的代码里,我定义了两个简单的中间件,分别用print输出中间件的名字,为的是研究中间件的调用顺序
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware("http")
async def before_request_1(request: Request, call_next):
print('before_request_1')
response = await call_next(request)
return response
@app.middleware("http")
async def before_request_2(request: Request, call_next):
print('before_request_2')
response = await call_next(request)
return response
@app.get('/index')
def index():
return 'ok'
定义中间件使用 @app.middleware("http") 装饰器,被装饰的函数接受两个参数
在浏览器里访问 http://127.0.0.1:8000/index, 服务端输出中间件名称的顺序是
before_request_2
before_request_1
由此可知,中间件的执行顺序与定义顺序是相反的。
如果想在请求被特定的路径操作函数处理之前做一些操作,例如检查发出请求的客户端IP是否符合白名单,那么就需要在调用call_next 之前做一些事情, 这样的功能在Flask框架里是通过before_request 装饰器实现的,现在我来实现一个相似的功能。
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.middleware("http")
async def check_remote_ip(request: Request, call_next):
print('check_remote_ip')
client_ip = request.client.host
if client_ip == '127.0.0.1':
return JSONResponse({"msg": 'bad request'})
response = await call_next(request)
return response
@app.get('/index')
def index():
return 'ok'
我定义一个检查客户端IP的中间件,如果ip不符合要求,立即返回检查结果,不会使用index函数处理请求。需要注意的是,在中间件里响应请求与在路径操作函数里响应请求是有不同的,在路径操作函数里返回字典,FastAPI会自动帮你转成json数据,而在中间件里,你必须自己实现这一点。
在请求得到路径操作处理之后,正式返回给客户端之前,如果你想对response做一些操作,仍然可以使用中间件,在Flask框架里通过after_request装饰器实现,下面我通过中间件来实现类似的功能
from datetime import datetime
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware("http")
async def add_access_cookie(request: Request, call_next):
print('add_access_cookie')
response = await call_next(request)
time_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
response.set_cookie(key='last_access_time', value=time_now)
return response
@app.get('/index')
def index():
return 'ok'
每个请求在返回结果之前,都会在响应对象里设置cookie, 实践中,有非常多的场景需要使用中间件,比如在请求结束之后关闭数据库连接,记录日志等等。
QQ交流群: 211426309