我们在开发 Web 应用的时候,处理程序意外情况是十分普遍的事情。在 HTTP 请求的整个处理链路中,当发生错误时,一种处理方式是在错误发生的地方自己捕获直接处理掉;另一种方式是抛出错误,交由上层处理。
错误拦截器
Malagu 框架提供了一种错误处理拦截器机制,通过实现接口 ErrorHandler
提供错误处理器,然后根据优先级将错误处理器排序,当发生错误的时候,错误依次传入错误处理器,直到找到匹配的错误处理器为止。
在错误处理器链的末端,框架提供了一个默认错误处理器,当错误处理器链前面的处理器都无法匹配该错误时,则交由末端默认错误处理器来处理。
通过错误处理拦截器机制,我们可以很容易将错误处理相关的代码与业务代码分离,让业务代码更加专注业务逻辑的编写,同时,我们也可以全局统一处理错误逻辑,从而保证整个应用错误处理风格的一致性。
注意事项
在错误处理器中,我们是通过 instanceof
语法来匹配错误是否由本错误处理器处理,默认 Node.js 提供的 Error 类无法使用 instanceof
语法来匹配,所以推荐使用框架提供 CustomError 基类来实现您自己的错误类。
import { CustomError } from '@malagu/core';
export class HttpError extends CustomError {
constructor(public statusCode: number, message?: string) {
super(message);
}
}
自定义错误处理器
当我们处理自定义错误,或者扩展框架内置的错误处理器时,我们需要实现自定义的错误处理器。首先,实现接口 ErrorHandler
,并将实现类注入到 IoC 容器,注入的 ID 为 ErrorHandler
。当发生错误时,框架通过 ErrorHandler
取到所有的错误处理器,并按优先级排序。如果您想替换掉某个错误处理器,只要确保您实现的优先级大于需要替换的错误处理器即可。
@Component(ErrorHandler)
export class HttpErrorHandler implements ErrorHandler {
readonly priority: number = HTTP_ERROR_HANDlER_PRIORITY;
canHandle(ctx: Context, err: Error): Promise<boolean> {
return Promise.resolve(err instanceof HttpError);
}
async handle(ctx: Context, err: HttpError): Promise<void> {
ctx.response.statusCode = err.statusCode;
ctx.response.end(err.message);
}
}
@Catch
在 mvc 组件中,我们提供了一个装饰器 @Catch()
帮助我们处理控制器当中抛出的异常。异常处理器方法必须定义在控制器当中,与 HTTP 请求处理器相似,不同的是异常处理器方法匹配的是具体的异常。异常处理器有以下规则:
- 只有控制器里面抛出的异常才会走异常映射流程
- 异常类必须继承 CustomError 基类,或者您提供的异常对象支持 instanceof 语法
@Catch
的使用不需要与抛异常的控制器是同一个- 异常控制器方法会继承抛异常的控制器方法的上下文,包括视图、请求的各种属性
@Controller()
export class ErrorController {
@Catch(FooError)
handle(err: FooError, @Body() body) {
...
}
}
内置错误类
CustomError
IllegalArgumentError
ValidationErrors
HttpError
NotFoundError
NotFoundAndContinueError
AuthenticationError
AccountStatusError
UsernameNotFoundError
BadCredentialsError
LockedError
DisabledError
AccountExpiredError
AccessDeniedError
OAuth2AuthorizationError
OAuth2AuthenticationError
ClientAuthorizationError
ClientAuthorizationRequiredError