除了 __call__,中间件还有三个钩子。它们各在请求生命周期的哪个位置?顺序一样吗?返回响应会怎样?这篇用三层带日志的中间件把答案直接打出来。
[普通视图] M1.__call__ 进入 M2.__call__ 进入 M3.__call__ 进入 M1.process_view(视图=ok) M2.process_view(视图=ok) M3.process_view(视图=ok) view 执行 M3.__call__ 返回 status=200 M2.__call__ 返回 status=200 M1.__call__ 返回 status=200 ==> 客户端得到 status = 200 body = b'ok'
所有中间件的 __call__ 先依次进入,此时 URL 已经解析,然后统一调用 process_view。所以 process_view 里能拿到 view_func 和参数,做「按视图决定是否放行」的事(比如 CSRF 就在这一层)。
[TemplateResponse] M1.__call__ 进入 M2.__call__ 进入 M3.__call__ 进入 M1.process_view(视图=tpl) M2.process_view(视图=tpl) M3.process_view(视图=tpl) view 返回 TemplateResponse(还没渲染) M3.process_template_response(rendered=False) M2.process_template_response(rendered=False) M1.process_template_response(rendered=False) M3.__call__ 返回 status=200 M2.__call__ 返回 status=200 M1.__call__ 返回 status=200 ==> 客户端得到 status = 200 body = b'hello 1'
process_template_response 只对有 render 方法的响应调用,此时还没渲染;可以在这里改 template_name、context_data。渲染发生在所有钩子之后。
[M2.process_view 返回响应]
M1.__call__ 进入
M2.__call__ 进入
M3.__call__ 进入
M1.process_view(视图=ok)
M2.process_view(视图=ok)
M2.process_view 直接返回响应
M3.__call__ 返回 status=203
M2.__call__ 返回 status=203
M1.__call__ 返回 status=203
==> 客户端得到 status = 203 body = b'from M2.process_view'返回响应就等于短路了视图,但因为 __call__ 已经全部进入过,响应仍会按洋葱顺序返回。
[三个 process_exception 都返回 None]
M1.__call__ 进入
M2.__call__ 进入
M3.__call__ 进入
M1.process_view(视图=bad)
M2.process_view(视图=bad)
M3.process_view(视图=bad)
view 抛出 ValueError
M3.process_exception(ValueError)
M2.process_exception(ValueError)
M1.process_exception(ValueError)
M3.__call__ 返回 status=500
M2.__call__ 返回 status=500
M1.__call__ 返回 status=500
==> 客户端得到 status = 500 body = b'\n<!doctype html>\n<html lang="e'
[M2.process_exception 返回响应]
M1.__call__ 进入
M2.__call__ 进入
M3.__call__ 进入
M1.process_view(视图=bad)
M2.process_view(视图=bad)
M3.process_view(视图=bad)
view 抛出 ValueError
M3.process_exception(ValueError)
M2.process_exception(ValueError)
M2.process_exception 返回了响应
M3.__call__ 返回 status=200
M2.__call__ 返回 status=200
M1.__call__ 返回 status=200
==> 客户端得到 status = 200 body = b'handled by M2'视图抛异常时按相反顺序询问每个中间件,第一个返回响应的就是最终结果。全都返回 None 时,异常继续向上,最终成为 500(第 17 篇)。