Как получить тело JSON-запроса POST в PHP?
Краткое решение
Основная проблема в том, что $_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.