Laravel5 : FormRequestバリデーション結果をjson APIで返す

概要

例えば、会員を新規追加するAPIのエンドポイントがあるとします。

[POST] https://example.com/api/users

routes/api.php

Route::resource( 'users', 'Api\UsersController' )->only( ['store'] );

Controllerは以下のような状態です。

app/Http/Controllers/Api/UsersController.php

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Requests\StoreUserRequest;
use App\Http\Resources\UserResource;
use App\User;

class UsersController extends Controller
{
    /**
     * 会員追加
     *
     * @param StoreUserRequest $request
     * @return User
     */
    public function store( StoreUserRequest $request )
    {
        return User::create( $request->only( ['name'] ) ); 
    }
}

FormRequestクラスは以下の状態です。

artisan

$ php artisan make:request StoreUserRequest

app/Http/Requests/StoreUserRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreUserRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'name' => 'required',
        ];
    }

    public function messages()
    {
        return [
            'name.required' => 'お名前を入力してください。',
        ];
    }
}

上記APIを叩いた際に「お名前を入力してください」と
JSON形式でエラーメッセージを返却したい場合を考えます。

このままの状態でAPIを叩いても素直にエラーレスポンスが返りません。
(welcomeページのHTMLが返ってきたりする)

原因と対応

バリデーション失敗時にFormRequestクラス内でリダイレクトが発生することが原因のようです。
参考: Laravel5.1.xでAPIを作る際に気になっていたことを調べました – Qiita

バリデーション失敗時にもJSON形式で結果を返すように対応していきます。

Laravel5.4以前

FormRequest::response() をオーバーライドすることで対応可能なようです。

StoreUserRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\JsonResponse;  // 追加

class StoreUserRequest extends FormRequest
{
    /**
     * [override] バリデーション失敗時ハンドリング
     *
     * @param array $errors
     * @return JsonResponse
     */
    public function response( array $errors )
    {
        $response = [
            'data'    => [],
            'status'  => 'NG',
            'summary' => 'Failed validation.',
            'errors'  => $errors,
        ];
        return new JsonResponse( $response, 422 );
    }
}

Laravel5.5以降

FormRequest::response() は廃止されているため、代わりにFormRequest::failedValidation() をオーバーライドすることで対応します。

StoreUserRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;  // 追加
use Illuminate\Http\Exceptions\HttpResponseException;  // 追加

class StoreUserRequest extends FormRequest
{
    /**
     * [override] バリデーション失敗時ハンドリング
     *
     * @param Validator $validator
     * @throw HttpResponseException
     * @see FormRequest::failedValidation()
     */
    protected function failedValidation( Validator $validator )
    {
        $response['data']    = [];
        $response['status']  = 'NG';
        $response['summary'] = 'Failed validation.';
        $response['errors']  = $validator->errors()->toArray();

        throw new HttpResponseException(
            response()->json( $response, 422 )
        );
    }
}

実行結果

画像は POSTMAN のキャプチャです。
エンドポイントを叩いてJSONレスポンスが返却されればOKです。

API用のリクエストクラスを作成する

APIを作成する場合はレスポンス形式を統一することになると思うので、下記のような抽象クラスを作成しておいたほうがよいでしょう。
Laravel5.8 FormRequestでバリデーション失敗時にJsonResponseを返す – Qiita

参考記事