Avatar ·

Как получить тело JSON-запроса POST в PHP?

Всем привет! Я новичок в PHP и пытаюсь написать простой REST API. Клиент отправляет мне POST-запрос с JSON-данными, но я никак не могу их получить на сервере. Вот как я пытаюсь это сделать: ```php $data]); ?> ``` Но при отправке запроса через Postman с заголовком Content-Type: application/json и телом {"name": "John", "age": 30} я получаю ошибку "Undefined index: data" или пустой массив. Я знаю, что $_POST работает с form-data и application/x-www-form-urlencoded, но как быть с JSON? Пробовал использовать file_get_contents('php://input'), но не уверен, что это правильно. Может быть, нужно что-то настраивать в php.ini? Или есть другие способы? Подскажите, пожалуйста, как правильно парсить JSON в PHP, чтобы я мог получить данные вроде name и age из тела запроса. Заранее спасибо за помощь! <<>>

Краткое решение

Основная проблема в том, что $_POST работает только с данными в формате application/x-www-form-urlencoded или multipart/form-data. Для JSON нужно читать сырой поток ввода php://input и декодировать его. Вот правильный способ:

<?php
// Получаем сырые данные из тела запроса
$json = file_get_contents('php://input');

// Декодируем JSON в ассоциативный массив (true - второй параметр)
$data = json_decode($json, true);

// Проверяем, что данные получены
if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
    http_response_code(400);
    echo json_encode(['error' => 'Некорректный JSON']);
    exit;
}

// Теперь можно работать с данными
$name = $data['name'] ?? 'не указано';
$age = $data['age'] ?? 'не указан';

echo json_encode([
    'received' => true,
    'name' => $name,
    'age' => $age
]);
?>

Подробное объяснение

  • php://input — это поток только для чтения, который позволяет получить сырые данные из тела запроса. Он работает для всех методов (POST, PUT, PATCH, DELETE) и не зависит от типа контента.
  • file_get_contents('php://input') читает весь поток в строку. Это подходит для небольших объёмов данных (до нескольких мегабайт).
  • json_decode($json, true) преобразует JSON-строку в ассоциативный массив. Если не передать true, получите объект stdClass.
  • Всегда проверяйте ошибки декодирования с помощью json_last_error(), чтобы избежать фатальных ошибок при невалидном JSON.

Полный пример с обработкой ошибок

<?php
header('Content-Type: application/json; charset=utf-8');

// Только для POST запросов
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['error' => 'Метод не поддерживается']);
    exit;
}

$input = file_get_contents('php://input');
$data = json_decode($input, true);

if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
    http_response_code(400);
    echo json_encode(['error' => 'Неверный формат JSON: ' . json_last_error_msg()]);
    exit;
}

// Пример использования
$response = [
    'success' => true,
    'name' => $data['name'] ?? 'Гость',
    'age' => $data['age'] ?? 0,
    'received_at' => date('Y-m-d H:i:s')
];

echo json_encode($response, JSON_UNESCAPED_UNICODE);
?>

Важные замечания

  • php://input можно прочитать только один раз. Если нужно использовать данные повторно, сохраните их в переменную.
  • Для больших файлов (загрузка через multipart/form-data) используйте $_FILES и $_POST, а не php://input.
  • В современных фреймворках (Laravel, Symfony) есть встроенные методы для работы с JSON-запросами, но базовый принцип остаётся тем же.
<<>>

Альтернативный подход: использование библиотек и обработка edge-кейсов

Если вы работаете с большими объёмами данных или часто имеете дело с JSON, рекомендую использовать специализированные инструменты:

<?php
// Вариант с использованием cURL (если вы сами отправляете запрос)
$ch = curl_init('https://example.com/api');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['key' => 'value']));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
?>

Также учтите несколько моментов:

  • Пустое тело запроса: file_get_contents('php://input') вернёт пустую строку. Проверяйте empty($input) и возвращайте ошибку 400.
  • Сжатие данных: если клиент отправляет сжатый JSON (gzip), нужно сначала декомпрессировать. Используйте gzdecode().
  • Безопасность: никогда не доверяйте входным данным. Всегда валидируйте и санируйте значения, особенно если они будут использоваться в SQL-запросах или выводиться на страницу.

Для микросервисов и простых API этого решения достаточно. Для сложных проектов рассмотрите использование готовых библиотек, например, symfony/http-foundation.