バリデーション

  • BEAR.SundayのバリデーションはJSONスキーマで行います。
  • Webフォームによるバリデーションはフォームをご覧ください。

JSONスキーマによるバリデーション

概要

JSON Schemaを使用して、リソースAPIの入出力仕様を定義し検証することができます。 これにより、APIの仕様を人間とマシンの両方が理解できる形式で管理できます。またApiDocとしてAPIドキュメントを出力することもできます。

セットアップ

モジュールの設定

バリデーションの適用範囲に応じて、以下のいずれかの方法で設定します:

  • すべての環境でバリデーションを行う場合:AppModuleに設定
  • 開発環境のみでバリデーションを行う場合:DevModuleに設定
use BEAR\Resource\Module\JsonSchemaModule;
use BEAR\Package\AbstractAppModule;

class AppModule extends AbstractAppModule
{
    protected function configure(): void
    {
        $this->install(
            new JsonSchemaModule(
                $appDir . '/var/json_schema',  // スキーマ定義用
                $appDir . '/var/json_validate' // バリデーション用
            )
        );
    }
}

2. 必要なディレクトリの作成

mkdir -p var/json_schema
mkdir -p var/json_validate

基本的な使用方法

1. リソースクラスの定義

use BEAR\Resource\Annotation\JsonSchema;

class User extends ResourceObject
{
    #[JsonSchema('user.json')]
    public function onGet(): static
    {
        $this->body = [
            'firstName' => 'mucha',
            'lastName' => 'alfons',
            'age' => 12
        ];
        return $this;
    }
}

2. JSONスキーマの定義

var/json_schema/user.json:

{
    "type": "object",
    "properties": {
        "firstName": {
            "type": "string",
            "maxLength": 30,
            "pattern": "[a-z\\d~+-]+"
        },
        "lastName": {
            "type": "string",
            "maxLength": 30,
            "pattern": "[a-z\\d~+-]+"
        }
    },
    "required": ["firstName", "lastName"]
}

高度な使用方法

インデックスキーの指定

レスポンスボディにインデックスキーがある場合、keyパラメータで指定します:

class User extends ResourceObject
{
    #[JsonSchema(key: 'user', schema: 'user.json')]
    public function onGet(): static
    {
        $this->body = [
            'user' => [
                'firstName' => 'mucha',
                'lastName' => 'alfons',
                'age' => 12
            ]
        ];
        
        return $this;
    }
}

引数のバリデーション

メソッドの引数をバリデーションする場合、paramsパラメータでスキーマを指定します:

class Todo extends ResourceObject
{
    #[JsonSchema(
        key: 'user',
        schema: 'user.json',
        params: 'todo.post.json'
    )]
    public function onPost(string $title)
    {
        // メソッドの処理
    }
}

var/json_validate/todo.post.json:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "/todo POST request validation",
    "properties": {
        "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
        }
    }
}

target

ResourceObjectのbodyに対してでなく、リソースオブジェクトの表現(レンダリングされた結果)に対してスキーマバリデーションを適用にするにはtarget='view'オプションを指定します。 HALフォーマットで_linkのスキーマが記述できます。

#[JsonSchema(schema: 'user.json', target: 'view')]

スキーマ作成支援ツール

JSONスキーマの作成には以下のツールが便利です:

例外ハンドリング

JSONスキーマバリデーションが失敗すると、BEAR.Resource (1.33.0+) は問題がリクエスト(クライアントエラー)にあるかレスポンス(サーバーのバグ)にあるかを示す型付き例外をスローします。

例外 HTTP 意味
JsonSchemaRequestException 400 入力パラメータがスキーマ検証に失敗
JsonSchemaResponseException 500 レスポンスボディがスキーマ検証に失敗

どちらも JsonSchemaException を継承しているため、個別にキャッチすることも、まとめてキャッチすることもできます。

構造化エラーへのアクセス

$e->getErrors()JsonSchemaErrors コレクションを返します。

use BEAR\Resource\Exception\JsonSchemaRequestException;
use BEAR\Resource\Exception\JsonSchemaResponseException;

try {
    $response = $resource->post('app://self/user', $params);
} catch (JsonSchemaRequestException $e) {
    $errors = $e->getErrors();        // JsonSchemaErrors
    $errors->count();                 // バリデーション違反の件数
    $errors->first();                 // 最初の JsonSchemaError または null

    // 各エラーを反復処理
    foreach ($errors as $error) {
        echo $error->property;        // 例: "email"
        echo $error->message;         // 人間が読めるメッセージ
    }

    // フィールド別にグループ化 — APIエラーレスポンス構築に便利
    $byField = $errors->byProperty(); // array<string, list<JsonSchemaError>>

    // テンプレートで一括フォーマット
    $text = $errors->format('{property}: {message}\n');
} catch (JsonSchemaResponseException $e) {
    // リソースが自身の宣言したスキーマに合わないデータを返した
    // サーバー側のバグとして扱う
    error_log((string) $e);
}

フレームワークのデフォルト動作

BEAR.Sunday 1.x のデフォルト ThrowableHandlerJsonSchemaRequestException を 400 の application/vnd.error+json レスポンスにマッピングします。API(JSON)コンテキストでは追加設定は不要です。

JSON vs HTML コンテキスト

適切なレスポンス形式はコンテキストによって異なります。

  • API / JSON コンテキスト — JSON エラーボディ(例: {"code": 400, "message": "..."})を返す
  • HTML コンテキスト — ブラウザユーザー向けに HTML エラーページをレンダリングする

本番アプリケーションでは、通常、各コンテキストに応じてデフォルトハンドラーをオーバーライドします。AppThrowableHandler(JSON)と HtmlThrowableHandler(HTML)を共有の ExceptionStatusMapper で使い分けるリファレンス実装は BEAR.Examples を参照してください。