Laravel - vynucení JSON response pro API

PHP, Laravel

Laravel často vrací HTTP response případně i přesměrování na API routes. Příkladem může být Error 404. Článek popisuje, jak donutit Laravel vracet vždy JSON response pro všechny API routy.

Laravel - vynucení JSON response pro API

Article in English can be found on dev.to/arxeiss/force-json-response-on-all-api-routes-in-laravel-29h

Popisovaný problém vzniká hlavně v situacích, kdy server vrací nějaký chybový kód. Převážně tedy 403 Unauthorized, 404 Not Found nebo při nevalidních datech 422 Unprocessable Entity. Situace je možné si jednoduše nasimulovat vložením následujícího kódu do routes/api.php a prováděním požadavků například přes Postman nebo prohlížeč.

use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::get('health-check', function () {
    return response()->json([ 'status' => 'OK', 'timestamp' => Carbon::now() ]);
});
Route::post('settings', function (Request $request) {
    $request->validate([ 'entry' => 'required|string|min:5' ]);
    return 'OK';
});

Na obrázku lze vidět, že ve všech situacích je response ze serveru ve formátu text/html, nikoli application/json. V některých situacích je uživatel dokonce přesměrován na úplně jinou stránku.

Ukázky HTML response na API routes v Laravelu

Jak vynutit JSON response

Laravel kontroluje hlavičku Accept v požadavku a podle té se rozhoduje, v jakém formátu odešle odpověď. Pokud je tedy přítomna hlavička Accept: application/json, odpověď bude odeslána ve formě JSON. Jenže browser či Postman často posílají hlavičku Accept: */*. Příchozí požadavek je tedy nutné upravit a tuto hlavičku přepsat. 

Vlastní middleware a route fallback

Middleware v tomto případě poslouží nejlépe. Stačí vytvořit nový soubor s obsahem níže v  app/Http/Middleware/ForceJsonResponse.php. Stejného efektu lze docílit také pomocí příkazu php artisan make:middleware ForceJsonResponse. Vytvořený middleware je pak nutné zaregistrovat pro všechny API routy v app/Http/Kernel.php. Posledním krokem je vytvoření fallback routy, tento krok není povinný, je ale vhodnější.

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class ForceJsonResponse
{
    public function handle(Request $request, Closure $next)
    {
        $request->headers->set('Accept', 'application/json');
        return $next($request);
    }
}
namespace App\Http;

class Kernel extends \Illuminate\Foundation\Http\Kernel
{
    ...
    protected $middlewareGroups = [
        ...
        'api' => [
            \App\Http\Middleware\ForceJsonResponse::class,
            ...
        ],
    ];
}
// routes/api.php
Route::fallback(function (){ abort(404, 'API resource not found'); });

I nadále může být vráceno HTML

Kód výše vyřeší chybové hlášky. Přesto Laravel vrátí HTML, pokud je z controlleru vrácen čistý  text. Tedy výsledek příkladu 3 na obrázku výše bude stále stejný. I tento problém je možné vyřešit middlewarem, detailnější popis je na blogu DarkGhostHunter. Osobně ale preferuji použít Response factory a vrátit opravdu JSON response. Tedy upravit kód jak je znázorněno níže.

Route::post('settings', function (Request $request) {
    $request->validate([ 'entry' => 'required|string|min:5' ]);
    // return 'OK';
return response()->json('OK'); });

Vlastní zkušenosti s API a JSON v Laravelu můžete sdílet v komentářích

Přidat komentář

Položky označené * jsou povinné. Email nebude zveřejněn

Buď první, kdo přidá komentář. Zatím zde nic není