Декодирование JSON из HTTP-ответов в Laravel

В Laravel 12.48.0 появилась удобная возможность точнее контролировать, как именно декодируются JSON-ответы HTTP-клиента.

Что происходит сейчас

Раньше многие разработчики сталкивались с неожиданным поведением:

$response = Http::get('https://cosmastech.com'); // URL, который возвращает HTML
$json = $response->json();

В этом примере в переменной $json оказывается null, и это вполне логично: по умолчанию PHP возвращает null, если переданный в json_decode() текст является невалидным JSON. Такая особенность известна не всем, пока не ломается код в продакшене.

Проверить, вернул ли json_decode() null из-за ошибки декодирования или потому что строка действительно содержит "null", можно через json_last_error(). Но это неудобно и громоздко.

Лучше выбрасывать исключение

В PHP 8 можно использовать флаг JSON_THROW_ON_ERROR, чтобы при попытке декодировать некорректный JSON не получить просто null, а бросить исключение \JsonException:

$str = '{"HELLO": xyz}'; // Некорректный JSON
json_decode($str, flags: JSON_THROW_ON_ERROR); // бросит исключение

Это безопаснее, потому что сразу видно, что что-то пошло не так.

Поддержка в Laravel

В Laravel теперь можно передать такие флаги прямо в методы разбора JSON у HTTP-ответа:

$response = Http::get('https://cosmastech.com');

try {
    $json = $response->json(flags: JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
    // Отлично — тут точно неверный JSON
}

Кроме метода .json() те же флаги принимают и другие методы ответа: .collect(), .object(), .fluent() что позволяет гибко управлять тем, как вы обрабатываете ответ.

Пример: большие целые числа

Если API возвращает очень большие числа, которые превышают максимальное значение целого типа в PHP (например, большие ID), без специальных флагов PHP превратит его в float. Чтобы сохранить такие числа как строки, можно использовать флаг JSON_BIGINT_AS_STRING:

Http::fake([
    '*' => '{
        "hello": "world",
        "big_int": 123343343580999843483023
    }'
]);

$response = Http::get('https://laravel.com');

$obj = $response->object(
    flags: JSON_BIGINT_AS_STRING
);

var_dump($obj->big_int); // "123343343580999843483023"

Без этого флага большое число стало бы float, и часть точности потерялась бы.

Настройки по умолчанию

Чтобы не передавать флаги в каждом вызове, можно задать дефолтные флаги декодирования в приложении. Для этого в AppServiceProvider в методе boot():

use Illuminate\Http\Client\Response;

public function boot()
{
    Response::$defaultJsonDecodingFlags =
        JSON_BIGINT_AS_STRING |
        JSON_THROW_ON_ERROR;
}

Теперь во всех вызовах .json() данные будут декодированы с этими флагами по умолчанию, и вам не придется явно указывать их в каждом месте.

Комментарии (0)

Войдите, чтобы оставить комментарий

Похожие статьи

Как использовать docker exec для запуска команд в контейнере Docker

Использование команды docker exec для запуска команд внутри работающего Docker-контейнера. Приведены примеры команд, вывод консоли, разбор опций и рекомендации по устранению ошибок.

12 0 1 мин

Типичные ошибки безопасности в Laravel-приложениях и как их правильно исправить

Распространённые ошибки безопасности в Laravel-приложениях и способы их устранения. Разбираем CSRF, SQL-инъекции, XSS, массовое заполнение, загрузку файлов и настройки окружения.

38 0 1 мин

Генерация PDF в Laravel: Spatie Laravel PDF vs Laravel DomPDF

Сравнение двух популярных пакетов для генерации PDF в Laravel, современного Spatie Laravel PDF с рендерингом через браузер и классического Laravel DomPDF, который работает в чистом PHP. Обсуждаем преимущества, примеры, ограничения и когда что использовать.

19 0 1 мин