Jak zdobyć ciało POST w php?

273

Przesyłam jako POST na stronie php:

{a:1}

To jest treść żądania (żądanie POST).
W php, co muszę zrobić, aby wyodrębnić tę wartość?

var_dump($_POST); 

nie jest rozwiązaniem, nie działa.

Itay Moav-Malimovka
źródło
23
Jest to pomocne pytanie dla osób, które chcą tworzyć interfejsy API RESTful. Większość nie wie, jak uzyskać dostęp do surowych danych wejściowych przesłanych do ich skryptów, ponieważ nie są one dostępne przez $_POSTsuperglobal. Jest to również (szczególnie) prawdziwe w przypadku żądań PUT, ponieważ PHP nie ma odpowiadającego superglobala.
rdlowrey
1
Warto zauważyć, że nazwa $ _POST jest myląca, ponieważ nie będzie tam żadnych danych z żądania POST, ale tylko wtedy, gdy typem treści jest application / x-www-form-urlencoded lub multipart / form-data
Petruza

Odpowiedzi:

549

Aby uzyskać dostęp do treści encji żądania POST lub PUT (lub dowolnej innej metody HTTP):

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

Ponadto STDINstała jest już otwartym strumieniem do php://input, więc możesz alternatywnie:

$entityBody = stream_get_contents(STDIN);

Z ręcznego wpisu PHP w dokumentach strumieni we / wy :

Dane wejściowe php: // to strumień tylko do odczytu, który umożliwia odczytanie surowych danych z treści żądania. W przypadku żądań POST lepiej jest używać danych wejściowych php: // zamiast, $HTTP_RAW_POST_DATAponieważ nie zależy to od specjalnych dyrektyw php.ini. Ponadto w przypadkach, w których $HTTP_RAW_POST_DATAdomyślnie nie jest zapełniany, jest to potencjalnie mniej obciążająca pamięć alternatywa dla aktywacji always_populate_raw_post_data. dane wejściowe php: // nie są dostępne z enctype = "multipart / form-data".

W szczególności należy zauważyć, że php://inputstrumień, niezależnie od tego, w jaki sposób uzyskujesz do niego dostęp w internetowym SAPI, nie jest widoczny . Oznacza to, że można go odczytać tylko raz. Jeśli pracujesz w środowisku, w którym duże jednostki HTTP są rutynowo przesyłane, możesz chcieć zachować dane wejściowe w formie strumienia (zamiast buforować je jak w pierwszym przykładzie powyżej).

Aby zachować zasób strumienia, pomocne może być coś takiego:

<?php

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

    return $tempStream;
}

php://temppozwala zarządzać zużyciem pamięci, ponieważ po przejściu pewnej ilości danych (domyślnie 2 MB) przełączy się na pamięć systemu plików. Rozmiarem tym można manipulować w pliku php.ini lub przez dołączenie /maxmemory:NN, gdzie NNjest maksymalna ilość danych przechowywanych w pamięci przed użyciem pliku tymczasowego, w bajtach.

Oczywiście, chyba że masz naprawdę dobry powód, aby szukać w strumieniu wejściowym, nie powinieneś potrzebować tej funkcji w aplikacji internetowej. Zwykle wystarczające jest przeczytanie treści jednostki żądania HTTP - nie zmuszaj klientów do czekania przez cały dzień, podczas gdy aplikacja wymyśli, co zrobić.

Zauważ, że wejście php: // nie jest dostępne dla żądań określających Content-Type: multipart/form-datanagłówek ( enctype="multipart/form-data"w formularzach HTML). Wynika to z parsowania przez PHP danych formularza na $_POSTsuperglobal.

rdlowrey
źródło
17
Należy pamiętać, że afaics, strumień STDIN nie jest dostępny w systemach z PHP korzystającym z CGI, tj. Przez mod_fcgid lub mod_fastcgi itp.
scy
ale przekazuję zmienną (jako dane formularza) z żądaniem, w jaki sposób mogę uzyskać dostęp do określonej wartości, przekazuję grant_type = hasło i nazwa użytkownika = użytkownik i hasło = podaj jako treść danych formularza z żądaniem, w jaki sposób otrzymam typ_zgody z „$ entityBody ”
Anvar Pk
według mojego testu php://inputjest to również puste dla typu application/x-www-form-urlencodedzawartości (poza tym multipart/form-data)
YakovL
6
Aby rozwinąć odpowiedź @ scy: STDIN jest niedostępny, ale php://inputjest. Tak więc, gdy konfiguracje CGI (szybkie) stream_get_contents(STDIN)nie będą działać, file_get_contents("php://input")będą działać .
Sinus Mackowaty
16

zwraca wartość w tablicy

 $data = json_decode(file_get_contents('php://input'), true);
umesh bhanderi
źródło
W tym scenariuszu musisz teraz przechodzić przez $datatablicę asocjacyjną, aby sprawdzić, czy każda wartość jest zakodowana w pożądany sposób. Sposób patrzenia na „typ strumienia danych” może być uproszczony, ale może nie być tak skuteczny, jak radzenie sobie z kodowaniem w „formie strumienia” za pomocą filtra strumienia. Jeśli nie zajmujesz się problemami z kodowaniem, a jedynie odkażaniem i sprawdzaniem poprawności, brakuje Ci kroku.
Anthony Rutledge
13

Możliwym powodem pustego $_POSTjest to, że żądanie nie jest POSTlub POSTjuż nie jest ... Być może zaczęło się jako post, ale napotkało gdzieś 301lub 302przekierowało, na które zostało przełączone GET!

Sprawdź, $_SERVER['REQUEST_METHOD']czy tak jest.

Zobacz https://stackoverflow.com/a/19422232/109787, aby uzyskać dobre omówienie, dlaczego tak się nie stało, ale nadal tak jest.

Legolas
źródło
1
To pytanie zostało zadane w kontekście rozwoju platformy API REST.
Itay Moav-Malimovka
Nie jestem pewny co masz na myśli? Interfejs API REST może równie dobrze znajdować przekierowania na swojej ścieżce, to był problem, który miałem.
Legolas,
Miałem na myśli, kiedy zadawałem pytanie, nie próbowałem rozwiązać problemu, a raczej próbowałem wymyślić, jak go rozwinąć.
Itay Moav-Malimovka
3
ta wskazówka uratowała mi dzień. serwer odbierający został ponownie skonfigurowany, aby przekierowywać na https, który zepsuł niektórych klientów API.
DesertEagle,
Właściwie moja prośba była, POSTale po sprawdzeniu okazała się, że była GET. Po dodaniu /na końcu mojego adresu URL zaczął on wyświetlać POST. Dziwne!
zackygaurav
4

Sprawdź $HTTP_RAW_POST_DATAzmienną

linepogl
źródło
7
Preferowaną metodą dostępu do surowych danych POST jest php://input. $HTTP_RAW_POST_DATAnie jest dostępny z enctype="multipart/form-data".
zerowanie
38
Ta funkcja została ODRADZANA w PHP 5.6.0 i USUNIĘTA od PHP 7.0.0.
Charles
3

Jeśli masz zainstalowane rozszerzenie HTTP PECL, możesz skorzystać z http_get_request_body()funkcji, aby uzyskać dane treści jako ciąg.

Shivanshu Patel
źródło
funkcja nie istnieje.
Rick,
2

Jeśli masz zainstalowane rozszerzenie pecl / http , możesz również użyć tego:

$request = new http\Env\Request();
$request->getBody();
spinkus
źródło
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
źródło
To, czego brakuje w tym logice, to test wartości znalezionej w nagłówku Content-Type. Nie wynika z tego, że tylko dlatego, że $ _POST jest pusty, JSON musiał zostać przesłany, lub jeśli json_last_error() == JSON_ERROR_NONEtak false, to że pusta tablica powinna zostać zwrócona. Co jeśli ktoś przesłał XML lub YAML? Dodaj test dla Content-Type i stamtąd.
Anthony Rutledge
Ponadto metoda żądania HTTP może brać pod uwagę to, czy chcesz zaakceptować dane wejściowe. Zobacz $_SERVERsuperglobal, aby sprawdzić przydatne wartości.
Anthony Rutledge