第11节,FastAPI 中间件

1. 定义FastAPI中间件

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") 装饰器,被装饰的函数接受两个参数

  1. request, 请求对象
  2. call_next ,一个函数,接收request对象并将其传给特定的路径操作函数

在浏览器里访问 http://127.0.0.1:8000/index, 服务端输出中间件名称的顺序是

before_request_2
before_request_1

由此可知,中间件的执行顺序与定义顺序是相反的。

2. before request

如果想在请求被特定的路径操作函数处理之前做一些操作,例如检查发出请求的客户端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数据,而在中间件里,你必须自己实现这一点。

3. after request

在请求得到路径操作处理之后,正式返回给客户端之前,如果你想对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

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案