中间件里怎么处理异常?一层直接返回响应会发生什么?这篇不背结论,用 M1、M2、M3 三层中间件加日志,在真实请求里把每种情形的调用顺序和每层看到的东西都记下来。
[三层都放行] M1 进入 M2 进入 M3 进入 view 执行 M3 收到响应 status=200 M2 收到响应 status=200 M1 收到响应 status=200 ==> 客户端得到 status = 200 body 前 40 字节 = b'ok' [M2 直接返回响应(短路)] M1 进入 M2 进入 M2 直接返回响应(短路) M1 收到响应 status=202 ==> 客户端得到 status = 202 body 前 40 字节 = b'short-circuit by M2'
M2 的短路在响应阶段仍会流过 M1,因此「在外层做统一的响应头 / 日志」对短路的请求同样有效。
[视图抛 ValueError(DEBUG=False)] M1 进入 M2 进入 M3 进入 view 抛出 ValueError M3 收到响应 status=500 M2 收到响应 status=500 M1 收到响应 status=500 ==> 客户端得到 status = 500 body 前 40 字节 = b'\n<!doctype html>\n<html lang="en">\n<head>' [视图抛 Http404] M1 进入 M2 进入 M3 进入 view 抛出 Http404 M3 收到响应 status=404 M2 收到响应 status=404 M1 收到响应 status=404 ==> 客户端得到 status = 404 body 前 40 字节 = b'\n<!doctype html>\n<html lang="en">\n<head>'
写 try: response = self.get_response(request) except Exception: 在新式中间件里通常是抓不到视图异常的:异常已经在更里层被转换成响应了。要在异常发生时做事,用 process_exception 钩子(第 18 篇)或 got_request_exception 信号(本文没测)。
[M2 在调用 get_response 之前抛出 ValueError] M1 进入 M2 进入 M2 抛出 ValueError M1 收到响应 status=500 ==> 客户端得到 status = 500 body 前 40 字节 = b'\n<!doctype html>\n<html lang="en">\n<head>'
M2 抛出的异常由 M1 外面的转换层处理,M1 收到 500 响应;M3 与视图不会执行。
[视图抛 ValueError(DEBUG=True)] M1 进入 M2 进入 M3 进入 view 抛出 ValueError M3 收到响应 status=500 M2 收到响应 status=500 M1 收到响应 status=500 ==> 客户端得到 status = 500 body 前 40 字节 = b'<!DOCTYPE html>\n<html lang="en">\n<head>\n'
同样的转换流程,正文换成调试页。