Middleware: 全局异常处理

我们在岩浆的实例其实已经注意到了,compose 的连接方式,让我们有能力精确控制异常。

Koa中间件最终行为强依赖注册顺序,比如我们这里要引入的异常处理,必须在业务逻辑中间件前注册,才能捕获后续中间件中未捕获异常,回想一下我们的调度器实现的异常传递流程。

  1. <?php
  2. class ExceptionHandler implements Middleware
  3. {
  4. public function __invoke(Context $ctx, $next)
  5. {
  6. try {
  7. yield $next;
  8. } catch (\Exception $ex) {
  9. $status = 500;
  10. $code = $ex->getCode() ?: 0;
  11. $msg = "Internal Error";
  12. // HttpException的异常通常是通过Context的throw方法抛出
  13. // 状态码与Msg直接提取可用
  14. if ($ex instanceof HttpException) {
  15. $status = $ex->status;
  16. if ($ex->expose) {
  17. $msg = $ex->getMessage();
  18. }
  19. }
  20. // 这里可这对其他异常区分处理
  21. // else if ($ex instanceof otherException) { }
  22. $err = [ "code" => $code, "msg" => $msg ];
  23. if ($ctx->accept("json")) {
  24. $ctx->status = 200;
  25. $ctx->body = $err;
  26. } else {
  27. $ctx->status = $status;
  28. if ($status === 404) {
  29. $ctx->body = (yield Template::render(__DIR__ . "/404.html"));
  30. } else if ($status === 500) {
  31. $ctx->body = (yield Template::render(__DIR__ . "/500.html", $err));
  32. } else {
  33. $ctx->body = (yield Template::render(__DIR__ . "/error.html", $err));
  34. }
  35. }
  36. }
  37. }
  38. }
  39. $app->υse(new ExceptionHandler());

可以将FastRoute与Exception中间件结合,很容可以定制一个按路由匹配注册的异常处理器,留待读者自行实现。

顺序问题再比如session中间件,需要优先于业务处理中间件,

而像处理404状态码的中间件,则需要在upstream流程中(逆序)的收尾阶段,我们仅仅只关心next后逻辑:

  1. <?php
  2. function notfound(Context $ctx, $next)
  3. {
  4. yield $next;
  5. if ($ctx->status !== 404 || $ctx->body) {
  6. return;
  7. }
  8. $ctx->status = 404;
  9. if ($ctx->accept("json")) {
  10. $ctx->body = [
  11. "message" => "Not Found",
  12. ];
  13. return;
  14. }
  15. $ctx->body = "<h1>404 Not Found</h1>";
  16. }