具有请求和响应的 Flask 中间件

Flask Middleware with both Request and Response

我想在 Flask 中创建一个中间件功能,用于记录请求 响应的详细信息。中间件应该 运行 在 Response 被创建之后,但在它被发回之前。我要登录:

  1. 请求的 HTTP 方法(GETPOSTPUT
  2. 请求端点
  3. 响应 HTTP 状态代码,包括 500 个响应。因此,如果在视图函数中引发异常,我想在 Flask 内部发送它之前记录生成的 500 响应。

我发现的一些选项(对我来说不太适用):

  1. before_request and after_request 装饰器。如果我可以访问 after_request 中的请求数据,我的问题仍然无法解决,因为根据文档

If a function raises an exception, any remaining after_request functions will not be called.

    本页描述的
  1. Deferred Request Callbacks - there is an after_this_request装饰器,装饰任意一个函数(定义在当前视图函数内部),并在当前请求后注册到运行。由于任意函数可以包含来自请求和响应的信息,因此它部分解决了我的问题。问题是我必须向每个视图函数添加这样一个装饰函数;我非常想避免这种情况。
@app.route('/')
def index():
    @after_this_request
    def add_header(response):
        response.headers['X-Foo'] = 'Parachute'
        return response
    return 'Hello World!'

有什么建议吗?

好吧,在 Deferred Request Callbacks 的页面上,答案一直盯着我看。

诀窍是在当前请求之后使用 after_this_request before_request 回调 中向 运行 注册一个函数。这是对我有用的代码片段:

@app.before_request
def log_details():
    method = request.method
    url = request.url

    @after_this_request
    def log_details_callback(response: Response):
        logger.info(f'method: {method}\n url: {url}\n status: {response.status}')

这些是步骤:

  1. before_request 回调中的响应中获取所需的详细信息,并将它们存储在一些变量中。
  2. 然后在您用 after_this_request 装饰的函数中访问您想要的响应,以及您之前存储请求详细信息的变量。

我的第一个回答很老套。实际上有一种更好的方法可以通过使用 g object in Flask. It is useful for storing information globally during a single request. From the documentation:

来实现相同的结果

The g name stands for “global”, but that is referring to the data being global within a context. The data on g is lost after the context ends, and it is not an appropriate place to store data between requests. Use the session or a database to store data across requests.

你会这样使用它:

@app.before_request
def gather_request_data():
    g.method = request.method
    g.url = request.url

@app.after_request
def log_details(response: Response):
    g.status = response.status

    logger.info(f'method: {g.method}\n url: {g.url}\n status: {g.status}')

    return response
  1. 在用 @app.before_request 装饰的函数中收集您想要的任何请求信息,并将其存储在 g 对象中。
  2. 从装饰有@app.after_request的函数中的响应中访问任何你想要的。您仍然可以参考步骤 1 中存储在 g 对象中的信息。请注意,您必须 return 此函数末尾的响应。

你可以使用 flask-http-middleware link

from flask import Flask
from flask_http_middleware import MiddlewareManager, BaseHTTPMiddleware

app = Flask(__name__)

class MetricsMiddleware(BaseHTTPMiddleware):
    def __init__(self):
        super().__init__()
    
    def dispatch(self, request, call_next):
        url = request.url
        response = call_next(request)
        response.headers.add("x-url", url)
        return response

app.wsgi_app = MiddlewareManager(app)
app.wsgi_app.add_middleware(MetricsMiddleware)

@app.get("/health")
def health():
    return {"message":"I'm healthy"}

if __name__ == "__main__":
    app.run()

每次请求都会通过中间件