异常处理
在 HTTP 请求期间抛出的异常由定义在 ./app/exceptions/handler.ts 文件中的 HttpExceptionHandler 处理。在这个文件中,你可以决定如何将异常转换为响应,以及如何使用日志记录器记录它们,或上报给外部日志提供商。
HttpExceptionHandler 继承自 ExceptionHandler 类,后者完成了处理错误的所有重活,并为你提供高层 API 来调整上报和渲染行为。
import app from '@adonisjs/core/services/app'
import { HttpContext, ExceptionHandler } from '@adonisjs/core/http'
export default class HttpExceptionHandler extends ExceptionHandler {
protected debug = !app.inProduction
protected renderStatusPages = app.inProduction
async handle(error: unknown, ctx: HttpContext) {
return super.handle(error, ctx)
}
async report(error: unknown, ctx: HttpContext) {
return super.report(error, ctx)
}
}
将错误处理器分配给服务器
错误处理器通过 start/kernel.ts 文件注册到 AdonisJS HTTP 服务器。我们使用在 package.json 文件中定义的 #exceptions 别名懒加载 HTTP 处理器。
server.errorHandler(() => import('#exceptions/handler'))
处理异常
异常由异常处理器类上的 handle 方法处理。默认情况下,处理错误时会执行以下步骤。
- 检查错误实例是否具有
handle方法。如果有,调用 error.handle 方法并返回其响应。 - 检查是否为
error.status状态码定义了状态页。如果有,渲染该状态页。 - 否则,使用内容协商渲染器渲染该异常。
如果你想以不同方式处理特定异常,可以在 handle 方法内完成。务必使用 ctx.response.send 方法发送响应,因为 handle 方法的返回值会被丢弃。
import { errors } from '@vinejs/vine'
export default class HttpExceptionHandler extends ExceptionHandler {
async handle(error: unknown, ctx: HttpContext) {
if (error instanceof errors.E_VALIDATION_ERROR) {
ctx.response.status(422).send(error.messages)
return
}
return super.handle(error, ctx)
}
}
状态页
状态页是你希望为给定状态码范围或一组状态码渲染的模板集合。
状态码范围可以定义为一个字符串表达式。起始和结束状态码用两个英文点号(..)分隔。
如果你正在创建 JSON 服务器,可能不需要状态页。
import { StatusPageRange, StatusPageRenderer } from '@adonisjs/http-server/types'
export default class HttpExceptionHandler extends ExceptionHandler {
protected statusPages: Record<StatusPageRange, StatusPageRenderer> = {
'404': (_, { view }) => view.render('errors/not-found'),
'500..599': (_, { view }) => view.render('errors/server-error')
}
}
调试模式
内容协商渲染器处理既未被自行处理、又未被转换为状态页的异常。
内容协商渲染器支持调试模式。在调试模式下,它们可以使用 Youch npm 包解析错误并将其美观打印出来。
你可以使用异常处理器类上的 debug 属性切换调试模式。不过,建议在生产环境中关闭调试模式,因为它会暴露你应用的敏感信息。
export default class HttpExceptionHandler extends ExceptionHandler {
protected debug = !app.inProduction
}
上报异常
异常处理器类上的 report 方法负责异常的上报。
该方法接收错误作为第一个参数,HTTP 上下文 作为第二个参数。你不应从 report 方法写入响应,只应使用上下文来读取请求信息。
记录异常
默认情况下,所有异常都会通过 logger 上报。
- 状态码在
400..499范围内的异常以warning级别记录。 - 状态码
>=500的异常以error级别记录。 - 所有其他异常以
info级别记录。
你可以通过从 context 方法返回一个对象,为日志消息添加自定义属性。
export default class HttpExceptionHandler extends ExceptionHandler {
protected context(ctx: HttpContext) {
return {
requestId: ctx.requestId,
userId: ctx.auth.user?.id,
ip: ctx.request.ip(),
}
}
}
忽略状态码
你可以通过 ignoreStatuses 属性定义一个状态码数组,让这些状态码的异常不被上报。
export default class HttpExceptionHandler extends ExceptionHandler {
protected ignoreStatuses = [
401,
400,
422,
403,
]
}
忽略错误
你也可以通过定义一个要忽略的错误码或错误类数组来忽略异常。
import { errors } from '@adonisjs/core'
import { errors as sessionErrors } from '@adonisjs/session'
export default class HttpExceptionHandler extends ExceptionHandler {
protected ignoreCodes = [
'E_ROUTE_NOT_FOUND',
'E_INVALID_SESSION'
]
}
可以使用 ignoreExceptions 属性忽略一组异常类。
import { errors } from '@adonisjs/core'
import { errors as sessionErrors } from '@adonisjs/session'
export default class HttpExceptionHandler extends ExceptionHandler {
protected ignoreExceptions = [
errors.E_ROUTE_NOT_FOUND,
sessionErrors.E_INVALID_SESSION,
]
}
自定义 shouldReport 方法
忽略状态码或异常的逻辑写在 shouldReport 方法 内部。如果需要,你可以覆盖此方法并定义自己的自定义逻辑来忽略异常。
import { HttpError } from '@adonisjs/core/types/http'
export default class HttpExceptionHandler extends ExceptionHandler {
protected shouldReport(error: HttpError) {
// 返回一个布尔值
}
}
自定义异常
你可以使用 make:exception ace 命令创建一个异常类。异常继承自 @adonisjs/core 包中的 Exception 类。
另请参阅:Make exception 命令
node ace make:exception UnAuthorized
import { Exception } from '@adonisjs/core/exceptions'
export default class UnAuthorizedException extends Exception {}
你可以通过创建它的新实例来抛出该异常。抛出异常时,你可以为异常分配自定义的错误码和状态码。
import UnAuthorizedException from '#exceptions/unauthorized_exception'
throw new UnAuthorizedException('You are not authorized', {
status: 403,
code: 'E_UNAUTHORIZED'
})
错误码和状态码也可以定义为异常类上的静态属性。如果在抛出异常时未定义自定义值,则会使用这些静态值。
import { Exception } from '@adonisjs/core/exceptions'
export default class UnAuthorizedException extends Exception {
static status = 403
static code = 'E_UNAUTHORIZED'
}
定义 handle 方法
要自行处理异常,你可以在异常类上定义 handle 方法。此方法应使用 ctx.response.send 方法将错误转换为 HTTP 响应。
error.handle 方法接收错误实例作为第一个参数,HTTP 上下文作为第二个参数。
import { Exception } from '@adonisjs/core/exceptions'
import { HttpContext } from '@adonisjs/core/http'
export default class UnAuthorizedException extends Exception {
async handle(error: this, ctx: HttpContext) {
ctx.response.status(error.status).send(error.message)
}
}
定义 report 方法
你可以在异常类上实现 report 方法,以自行处理异常的上报。report 方法接收错误实例作为第一个参数,HTTP 上下文作为第二个参数。
import { Exception } from '@adonisjs/core/exceptions'
import { HttpContext } from '@adonisjs/core/http'
export default class UnAuthorizedException extends Exception {
async report(error: this, ctx: HttpContext) {
ctx.logger.error({ err: error }, error.message)
}
}
缩小错误类型
框架核心和其他官方包会导出它们抛出的异常。你可以使用 instanceof 检查来验证某个错误是否为特定异常的实例。例如:
import { errors } from '@adonisjs/core'
try {
router.builder().make('articles.index')
} catch (error: unknown) {
if (error instanceof errors.E_CANNOT_LOOKUP_ROUTE) {
// 处理错误
}
}
已知错误
请查看 异常参考指南 了解已知错误列表。