Как получить тело POST в php?

273

Я отправляю как POST на страницу php следующее:

{a:1}

Это тело запроса (запрос POST).
В php, что мне нужно сделать, чтобы извлечь это значение?

var_dump($_POST); 

это не решение, не работает.

Итай Моав -Малимовка
источник
23
Это полезный вопрос для тех, кто хочет создавать RESTful API. Большинство не знает, как получить доступ к необработанным входным данным, представленным в их сценариях, поскольку они не доступны через $_POSTсуперглобальный объект. Это также (особенно) верно в случае запросов PUT, поскольку в PHP нет соответствующего суперглобального.
rdlowrey
1
Стоит отметить, что имя $ _POST вводит в заблуждение, поскольку там не будут присутствовать данные любого типа из запроса POST, но только в том случае, если тип контента - application / x-www-form-urlencoded или multipart / form-data
Petruza

Ответы:

549

Чтобы получить доступ к телу объекта запроса POST или PUT (или любого другого метода HTTP):

$entityBody = file_get_contents('php://input');

Кроме того, STDINконстанта является уже открытым потоком php://input, так что вы можете сделать следующее:

$entityBody = stream_get_contents(STDIN);

Из руководства по PHP для документации потоков ввода / вывода :

php: // input - это поток только для чтения, который позволяет вам читать необработанные данные из тела запроса. В случае запросов POST предпочтительно использовать ввод php: // вместо того, $HTTP_RAW_POST_DATAчтобы не зависеть от специальных директив php.ini. Более того, для тех случаев, когда $HTTP_RAW_POST_DATAпо умолчанию не заполняется, это потенциально менее ресурсоемкая альтернатива активации always_populate_raw_post_data. php: // ввод недоступен с enctype = "multipart / form-data".

В частности, вы должны заметить, что php://inputпоток, независимо от того, как вы обращаетесь к нему в веб-SAPI, не доступен для поиска . Это означает, что его можно прочитать только один раз. Если вы работаете в среде, в которой регулярно загружаются большие объекты сущностей HTTP, вы можете сохранить входные данные в виде потока (вместо того, чтобы буферизовать их, как в первом примере выше).

Для поддержки потокового ресурса может быть полезно что-то вроде этого:

<?php

function detectRequestBody() {
    $rawInput = fopen('php://input', 'r');
    $tempStream = fopen('php://temp', 'r+');
    stream_copy_to_stream($rawInput, $tempStream);
    rewind($tempStream);

    return $tempStream;
}

php://tempпозволяет управлять потреблением памяти, поскольку оно прозрачно переключится на хранилище файловой системы после сохранения определенного объема данных (по умолчанию 2M). Этим размером можно манипулировать в файле php.ini или путем добавления /maxmemory:NN, где указан NNмаксимальный объем данных, сохраняемых в памяти перед использованием временного файла, в байтах.

Конечно, если у вас нет действительно веских причин для поиска во входном потоке, вам не понадобятся эти функции в веб-приложении. Как правило, достаточно прочитать тело сущности HTTP-запроса один раз - не заставляйте клиентов ждать весь день, пока ваше приложение выяснит, что делать.

Обратите внимание, что php: // input недоступен для запросов, указывающих Content-Type: multipart/form-dataзаголовок ( enctype="multipart/form-data"в HTML-формах). Это результат того, что PHP уже проанализировал данные формы в $_POSTсуперглобальном.

rdlowrey
источник
17
Обратите внимание, что на самом деле поток STDIN недоступен в системах, использующих PHP с использованием CGI, т.е. через mod_fcgid или mod_fastcgi и т. Д.
scy
но, я передаю переменную (как данные формы) с запросом, как я могу получить доступ к указанному значению, я передаю grant_type = пароль и имя пользователя = user & пароль = передать как тело формы данных с запросом, как я получу grant_type из "$ entityBody "
Anvar Pk
согласно моему тесту, это также php://inputпусто для application/x-www-form-urlencodedтипа контента (кроме multipart/form-data)
YakovL
6
Чтобы расширить ответ @ scy: STDIN не доступен, но php://inputесть. Так что пока (быстро) конфигурации CGI stream_get_contents(STDIN)не будут работать, file_get_contents("php://input")будет.
Синус Макковаты
16

возвращаемое значение в массиве

 $data = json_decode(file_get_contents('php://input'), true);
умеш бхандери
источник
В этом сценарии вам теперь нужно перебрать $dataассоциативный массив, чтобы проверить, закодировано ли каждое значение так, как вы хотите. Взгляд на поток с типом данных может быть упрощенным, но он может быть не таким эффективным, как работа с кодированием в «потоковой форме» с использованием потокового фильтра. Если вы не решаете проблемы с кодированием, а просто очищаете и проверяете, вы пропускаете шаг.
Энтони Ратледж,
13

Возможная причина пустого $_POSTзаключается в том, что запрос не является POST, или нет POSTбольше ... Возможно, он начинался как пост, но где-то встретил 301или 302перенаправил, который переключен на GET!

Осмотрите, $_SERVER['REQUEST_METHOD']чтобы проверить, так ли это.

См. Https://stackoverflow.com/a/19422232/109787 для хорошего обсуждения того, почему это не должно происходить, но все же происходит.

Леголас
источник
1
Этот вопрос был задан в контексте разработки платформы REST API.
Итай Моав -Малимовка
Я не уверен, что ты имеешь в виду? REST API может также найти редиректы на своем пути, это проблема, с которой я столкнулся.
Леголас
Я имел в виду, когда я задавал вопрос, я не пытался устранить ошибку, а пытался выяснить, как ее развить.
Итай Моав -Малимовка
3
этот намек спас мой день. принимающий сервер был перенастроен для перенаправления на https, который сломал некоторые клиенты API.
DesertEagle
На самом деле моя просьба была, POSTно после проверки она показала, что это так GET. Как только я добавил /в конце своего URL, он начал показывать POST. Weird!
Закигаурав
4

Проверьте $HTTP_RAW_POST_DATAпеременную

linepogl
источник
7
Предпочтительным методом доступа к необработанным данным POST является php://input. $HTTP_RAW_POST_DATAнедоступно с enctype="multipart/form-data".
обнуляемость
38
Эта функция УСТАРЕЛА в PHP 5.6.0 и УДАЛЕНА с PHP 7.0.0.
Чарльз
3

Если вы установили расширение HTTP PECL, вы можете использовать http_get_request_body()функцию для получения данных тела в виде строки.

Шиваншу Патель
источник
функция не существует
Рик
2

Если у вас установлено расширение pecl / http , вы также можете использовать это:

$request = new http\Env\Request();
$request->getBody();
spinkus
источник
2
function getPost()
{
    if(!empty($_POST))
    {
        // when using application/x-www-form-urlencoded or multipart/form-data as the HTTP Content-Type in the request
        // NOTE: if this is the case and $_POST is empty, check the variables_order in php.ini! - it must contain the letter P
        return $_POST;
    }

    // when using application/json as the HTTP Content-Type in the request 
    $post = json_decode(file_get_contents('php://input'), true);
    if(json_last_error() == JSON_ERROR_NONE)
    {
        return $post;
    }

    return [];
}

print_r(getPost());
Hatzegopteryx
источник
Чего не хватает этой логике, так это проверки значения, найденного в заголовке Content-Type. Из этого не следует , что только потому , что $ _POST пусто , что JSON должен быть представлен, или если json_last_error() == JSON_ERROR_NONEесть false, что пустой массив должен быть возвращен. Что если кто-то отправил XML или YAML? Добавьте тест для Content-Type и перейдите оттуда.
Энтони Ратледж
Кроме того, метод HTTP-запроса может учитывать, хотите ли вы принять входные данные. Смотрите $_SERVERsuperglobal для полезных значений для проверки.
Энтони Ратледж