Laravel 5.5 вывод ошибок валидации для API

Laravel 5.5 вывод ошибок валидации для API

Laravel является очень удачным инструментом для написания собственного API. В этом фреймворке уже предусмотрено всё для простой разработки API. К сожалению, ошибки в приложении бывают часто, и, потому, важно знать, как их можно обработать и изменить формат вывода. В этой статье я покажу, как можно кастомизировать вывод ошибок валидации, и отдавать в удобном для нас формате, в JSON.

Использование форм-реквестов позволяет вынести отвественность валидации данных в отдельный класс. Однако, теперь, когда валидация запроса переложена на отдельный класс, то и обработка ошибок производится в нём же. И, иногда, стандартного поведения обработки ошибок валидации оказывается недостаточно, потому и появляются подобные статьи.

Чтобы вы понимали, о чём я сейчас говорю, например, при отправке запроса к API, ошибки валидации придут в подобном формате:
error-default-form-request

Обработка исключений из единого места

Читая документация Laravel, можно понять, что существует единое место для обработки исключений - app/Http/Exceptions/Handler.php.
Это файл, в котором можно отловить исключения нужного типа (или все без разбора), и обработать их. Метод render отвечает за то, в каком виде будут отображены сообщения исключений.

Изначально этот метод содержит код:

public function render($request, Exception $exception)
{
    return parent::render($request, $exception);
}

Но можно его немного модифицировать, добавив правило: если приходит AJAX запрос, или запрос содержит заголовок Accept: application/json, то, отправим ответ в формате JSON

public function render($request, Exception $exception)
{
    if ($request->ajax() || $request->wantsJson())
    {
        $json = [
            'success' => false,
            'error' => [
                // отправляем сообщение исключения
                'messages' => $exception->getMessage(),
            ]
        ];
        // иначе обработка, что была раньше
        return response()->json($json, 400);
    }

    return parent::render($request, $exception);
}

В результате, получим ответ, который не показывает все ошибки валидации. В этом случае при любой из ошибок всегда будет одно сообщение "The given data was invalid." (в случае валидации):
habdler.php

Но, как я писал выше, можно отловить исключения только определённого типа. В данном случае, нас интересуют только исключения валидации, экземпляра ValidationException.

Модифицируем код:

public function render($request, Exception $exception)
{
    if ($request->ajax() || $request->wantsJson())
    {
        // если это не ошибка валидации, то пишем сообщение, полученное из экземпляра исключения
        $message = [$exception->getMessage()];

        // если ошибка валидации
        if($exception instanceof ValidationException) {
            $message = $exception->errors();
        }

        $json = [
            'success' => false,
            'error' => [
                'messages' => $message,
            ],
        ];
        
        // возвращаем массив ошибок
        return response()->json($json, 400);
    }

    return parent::render($request, $exception);
}

В этом коде мы проверяем, является ли объект исключения экземпляром ValidationException, который как раз и содержит в себе информацию об ошибках валидации.validation

Вот такой формат уже приемлем для того, чтобы удобно их обрабатывать на клиенте. Дальнейшая обработка зависит от ваших требования и задач.

Вынесение логики обработки исключений в отдельный Form Request

Гораздо лучше было бы создать правила, примениемые только к некоторым форм-реквестам. И, мы можем это сделать.

Для этого, в папке app/Http/Requests создадим свою реализацию FormRequest-класса, который будет возвращать ошибки валидации в JSON-е:

namespace App\Http\Requests;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException;
use Illuminate\Foundation\Http\FormRequest;

abstract class ApiFormRequest extends FormRequest
{
    protected function failedValidation(Validator $validator)
    {
        // все ошибки валидации
        $errors = (new ValidationException($validator))->errors();

        throw new HttpResponseException(response()->json([
            'errors' => $errors
        ], JsonResponse::HTTP_UNPROCESSABLE_ENTITY));
    }
    
    abstract public function authorize();
    abstract public function rules();
}

И теперь, любой FormRequest-класс, в котором нужен вывод ошибок в JSON, наследуем от этого класса, например:

namespace App\Http\Requests\Auth;

use App\Http\Requests\ApiFormRequest;

class RegisterFormRequest extends ApiFormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [];
    }
}

И в итоге, получим данные в формате:
api-form-request

В версиях Laravel ниже 5.5, метод называется response а не failedValidation.

Резюме

В этой статье я показал, как кастомизировать Laravel FormRequest валидацию. Так же, на примере, показал, как сделать вывод ошибок валидации при API запросах. Здесь был рассмотрен базовый подход по тому, как можно изменить формат вывода исключений в Laravel. И, так же, по примеру, вы с легкостью сможете реализовать любой формат вывода, который нужен.