例外処理

例外処理

HTTPリクエスト中に発生した例外は、./app/exceptions/handler.tsファイル内で定義されたHttpExceptionHandlerによって処理されます。このファイル内では、例外をレスポンスに変換し、ロガーを使用してログに記録するか、外部のログプロバイダに報告する方法を決定できます。

HttpExceptionHandlerExceptionHandlerクラスを拡張しており、エラーの処理とレンダリングの振る舞いを調整するための高レベルの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)
}
}

サーバーにエラーハンドラを割り当てる

エラーハンドラは、AdonisJSのHTTPサーバーにstart/kernel.tsファイル内で登録されます。package.jsonファイルで定義された#exceptionsエイリアスを使用して、HTTPハンドラを遅延読み込みしています。

server.errorHandler(() => import('#exceptions/handler'))

例外の処理

例外は例外ハンドラクラスのhandleメソッドによって処理されます。デフォルトでは、エラーの処理中には次のステップが実行されます。

  • エラーインスタンスにhandleメソッドがあるかどうかをチェックします。ある場合は、error.handleメソッドを呼び出してそのレスポンスを返します。
  • error.statusコードに対してステータスページが定義されているかどうかをチェックします。ある場合は、ステータスページをレンダリングします。
  • それ以外の場合は、コンテンツネゴシエーションレンダラを使用して例外をレンダリングします。

特定の例外を異なる方法で処理したい場合は、handleメソッド内でそれを行うことができます。ただし、handleメソッドの戻り値は破棄されるため、レスポンスを送信するためにctx.response.sendメソッドを使用することを確認してください。

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)
}
}

ステータスページ

ステータスページは、特定のステータスコードまたはステータスコードの範囲に対してレンダリングするテンプレートのコレクションです。

ステータスコードの範囲は文字列式で定義できます。開始と終了のステータスコードは2つのドット(..)で区切られます。

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メソッドは、例外の報告を処理します。

このメソッドは、最初の引数としてエラー、2番目の引数としてHTTPコンテキストを受け取ります。reportメソッドからレスポンスを書き込むことはせず、リクエスト情報を読み取るためにコンテキストのみを使用するようにしてください。

例外のログ記録

デフォルトでは、すべての例外はロガーを使用して報告されます。

  • 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,
]
}

エラーの無視

ignoreCodesプロパティを使用して、無視するエラーコードまたはエラークラスの配列を定義することもできます。

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) {
// booleanを返す
}
}

カスタム例外

make:exceptionエースコマンドを使用して、例外クラスを作成できます。例外は@adonisjs/coreパッケージの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メソッドは、最初の引数としてエラーのインスタンス、2番目の引数として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メソッドは、最初の引数としてエラーのインスタンス、2番目の引数として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) {
// エラーを処理する
}
}

既知のエラー

既知のエラーのリストについては、例外リファレンスガイドを参照してください。