Api w Laravelu – zwracanie odpowiedzi zamiast przekierowań

Dawno mnie tu nie było, bo byłem na urlopie a zaraz po nim wskoczyłem do nowego projektu i jakiś czas zastanawiałem się o czym napisać. Natrafiłem na jedną rzecz, której użyłem już w kilku projektach, dlatego myślę, że warto o tym napisać.

Jeśli robiłeś API w Laravelu, możliwe że spotkałeś się z takim zachowaniem, że gdy walidacja requestu nie przechodzi, wykonywane jest przekierowanie. Zamiast tego, lepiej byłoby dostać w odpowiedzi JSON z informacją o błędzie. Jak to zrobić? Już pokazuję.

1. W katalogu app utwórz katalog Factories a w nim plik ApiResponseFactory.php o treści:

<?php
namespace App\Factories;

use Illuminate\Http\Response;
use Illuminate\Support\MessageBag;

/**
 * Class ApiResponseFactory
 * @package App\Http\Responses
 */
class ApiResponseFactory
{
    const RESPONSE_FIELD_ERRORS = 'errors';
    const MESSAGE_ENTITY_NOT_FOUND = 'Entity not found.';

    /**
     * @param MessageBag|array|string $errors
     * @return Response
     */
    public static function badRequest($errors)
    {
        return response(
            [
                self::RESPONSE_FIELD_ERRORS => $errors
            ],
            Response::HTTP_BAD_REQUEST
        );
    }

    /**
     * @return Response
     */
    public static function notFound()
    {
        return response(
            [
                self::RESPONSE_FIELD_ERRORS => self::MESSAGE_ENTITY_NOT_FOUND
            ],
            Response::HTTP_NOT_FOUND
        );
    }
}

2. W katalogu app utwórz katalog Exceptions a w nim plik Handler.php o treści:

<?php

namespace App\Exceptions;

use App\Factories\ApiResponseFactory;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that are not reported.
     *
     * @var array
     */
    protected $dontReport = [
        //
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array
     */
    protected $dontFlash = [
        'password',
        'password_confirmation',
    ];

    /**
     * Report or log an exception.
     *
     * @param  \Exception  $exception
     * @return void
     */
    public function report(Exception $exception)
    {
        parent::report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {
        switch (true)
        {
            case $exception instanceof ModelNotFoundException:
                return ApiResponseFactory::notFound();

            case $exception instanceof ValidationException:
                return ApiResponseFactory::badRequest($exception->validator->errors());

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

I tyle wystarczy, żeby odpowiedź Http wyglądała mniej-więcej tak:

{
    "errors": {
        "fromCity.id": [
            "The city name field is required."
        ]
    }
}

Dodatkowo nasz handler będzie wyłapywał wyjątki typu ModelNotFoundException, które pojawiają się, kiedy szukamy encji przy pomocy metody findOfFail.

Jeśli do autentykacji w Twoim API używasz Laravel Passport, musisz pamiętać o wysyłaniu headera Accept: application/json we wszystkich requestach. Bez tego nadal będziesz otrzymywać przekierowania stronę logowania.

Udostępnij
Tags:,

Skomentuj