Laravel вирішує, як відповісти на виняток - HTML-сторінкою чи JSON, - за заголовком Accept запиту ($request->expectsJson()).
Помилка валідації для запиту з Accept: application/json:
HTTP/1.1 422 Unprocessable Content
{
"message": "The email field is required. (and 1 more error)",
"errors": {
"email": ["The email field is required."],
"password": ["The password field must be at least 8 characters."]
}
}
Без Accept: application/json та сама помилка валідації - це редирект назад (302) з помилками в сесії, як для HTML-форм. Клієнт API отримає HTML сторінки замість зрозумілої помилки. Найчастіша причина «API повертає 302 замість 422».
Інші типові відповіді:
| Ситуація | Статус |
|---|---|
немає чи недійсний токен (AuthenticationException) |
401 {"message": "Unauthenticated."} |
authorize() / політика відмовила |
403 |
модель не знайдено (findOrFail, прив'язка маршруту) |
404 |
перевищено ліміт (throttle) |
429 з Retry-After |
| виняток у коді | 500 |
APP_DEBUG=true додає до відповіді 500 повідомлення винятку, файл, рядок і стек. На продакшені - обов'язково false, інакше API розкриває внутрішню будову коду.
Примусово JSON для всіх маршрутів API - незалежно від заголовка клієнта:
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->shouldRenderJsonWhen(
fn (Request $request, Throwable $e) => $request->is('api/*') || $request->expectsJson(),
);
})
Власний формат для конкретного винятку:
$exceptions->render(function (OrderAlreadyShippedException $e, Request $request) {
return response()->json(['message' => 'Замовлення вже відправлено'], 409);
});
Для клієнтів API варто задокументувати: завжди надсилати Accept: application/json, а формат помилки - єдиний для всіх ендпойнтів.
Докладніше в документації: Laravel: рендеринг винятків як JSON