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.
Skomentuj