💚
LaravelでRFC9457に準拠したエラーレスポンスを実装する方法
🔎 RFC9457とは
RFC9457は、HTTP APIでエラーが発生した際に、エラーレスポンスを統一的なフォーマットで返すための仕様(標準)を定めた文書です。
これに従うことで、API利用者はどのエラーも同じ形式で受け取れるため、エラーの内容を機械的・人的に理解しやすくなります。
なぜRFC9457に準拠するのか?
- 一貫性あるエラーフォーマット:APIの開発者や利用者が、どんなエラーでも同じ枠組みで解析できます。
- 明確なエラー情報:エラーの種類、タイトル、詳細説明などが明確に定義されているため、問題解決が容易になります。
📝 カスタム例外インターフェースの作成
まずは、RFC9457形式でエラーレスポンスを返すために必要なメソッドを定義した、インターフェースを用意します。
<?php
// app/Exceptions/ApiExceptionInterface.php
namespace App\Exceptions;
use Throwable;
interface ApiExceptionInterface extends Throwable
{
public function getType(): string; // エラータイプ(URI形式)
public function getTitle(): string; // エラータイトル
public function getStatusCode(): int; // HTTPステータスコード
public function getDetail(): string; // 詳細なエラー説明
}
このインターフェースを実装することで、どのカスタム例外でも同じ項目を揃えられます。
🛠️ カスタム例外クラスの実装
次に、上記インターフェースを実装した「カスタム例外クラス」を作成します。
ここでは ApiException というクラスを例にします。
<?php
// app/Exceptions/ApiException.php
namespace App\Exceptions;
use Exception;
class ApiException extends Exception implements ApiExceptionInterface
{
public function getType(): string
{
return 'https://example.com/api-error';
}
public function getTitle(): string
{
return 'Api Error';
}
public function getStatusCode(): int
{
return 500;
}
public function getDetail(): string
{
return 'Unexpected error occurred.';
}
}
このクラスは、エラーが発生した時に固定的なタイトルや詳細を返しますが、実際にはコンストラクタで動的にメッセージを受け取り、それを getDetail() で返すようにすることで、より柔軟に扱えます。
🔥 例外のスロー
コントローラー内でエラーが起きたら、上記のカスタム例外を投げます。
これにより、後述の例外ハンドラーがRFC9457形式でレスポンスを返します。
<?php
// app/Http/Controllers/ExampleController.php
namespace App\Http\Controllers;
use App\Exceptions\ApiException;
use Illuminate\Http\Request;
class ExampleController extends Controller
{
public function doSomething(Request $request)
{
try {
// 何らかの処理
// ...
// エラー発生時に例外をスロー
throw new ApiException('予期しないエラーが発生しました');
} catch (ApiException $e) {
// ここでは再スローしないで例外ハンドラーに任せる
throw $e;
}
}
}
⚙️ 例外ハンドリングの設定
Laravelには例外を処理するための仕組み(エラーハンドラー)が用意されています。ここに、 ApiExceptionInterface を実装している例外が投げられたら、RFC9457の形式でJSONレスポンスを返すようにします。
<?php
// bootstrap/app.php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use App\Exceptions\ApiExceptionInterface;
use Illuminate\Support\Facades\Response;
return Application::configure(basePath: dirname(__DIR__))
->withExceptions(function (Exceptions $exceptions) {
// ApiExceptionInterfaceを実装する例外の処理方法を定義
$exceptions->renderable(function (ApiExceptionInterface $exception, $request) {
return Response::json([
'type' => $exception->getType(), // エラー種別のURI
'title' => $exception->getTitle(), // エラータイトル
'status' => $exception->getStatusCode(), // HTTPステータスコード
'detail' => $exception->getDetail(), // エラー詳細メッセージ
'instance'=> $request->path(), // 発生したエンドポイントのパス
], $exception->getStatusCode());
});
});
✍️ まとめ
- カスタム例外インターフェース:RFC9457形式でエラー情報を提供するための約束事を定義。
- カスタム例外クラス:インターフェースを実装し、統一的なエラーレスポンスを返す準備。
- コントローラーでのスロー:実際のビジネスロジックでエラーが起きたら、カスタム例外を投げる。
- 例外ハンドラーでの処理:カスタム例外をキャッチし、RFC9457に準拠したJSONレスポンスを返す。
こうすることで、APIのエラー応答がわかりやすくなり、API利用者は発生した問題をより簡単に理解・解決できます。
Discussion