如何在php中获取POST正文?


273

我将以下内容作为POST提交到php页面:

{a:1}

这是请求(POST请求)的主体。
在php中,我该怎么做才能提取该值?

var_dump($_POST); 

不是解决方案,不起作用。


23
对于希望创建RESTful API的人们来说,这是一个有用的问题。大多数人不知道如何访问提交给脚本的原始输入数据,因为它们无法通过$_POSTsuperglobal获得。在PUT请求的情况下也是如此(尤其是),因为PHP没有相应的超全局变量。
rdlowrey'1


1
值得注意的是,名称$ _POST具有误导性,因为那里不会包含POST请求中的任何类型的数据,但仅当内容类型为application / x-www-form-urlencodedmultipart / form-data
时才出现

Answers:


549

要访问POST或PUT请求(或任何其他HTTP方法)的实体主体,请执行以下操作:

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

另外,该STDIN常量是的已经开放的流php://input,因此您可以选择执行以下操作:

$entityBody = stream_get_contents(STDIN);

在I / O流docsPHP手册条目中

php:// input是一个只读流,允许您从请求正文中读取原始数据。对于POST请求,最好使用php:// input代替,$HTTP_RAW_POST_DATA因为它不依赖于特殊的php.ini指令。此外,对于$HTTP_RAW_POST_DATA默认情况下未填充的那些情况 ,它是激活always_populate_raw_post_data的一种潜在的内存密集型替代方法。php:// input不适用于enctype =“ multipart / form-data”。

特别要注意的是php://input,无论您如何在Web 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是使用临时文件之前要保留在内存中的最大数据量(以字节为单位)。

当然,除非您有充分的理由在输入流中进行搜索,否则您不应该在Web应用程序中使用此功能。通常,一次读取HTTP请求实体主体就足够了-不要让客户端整天等待您的应用弄清楚该怎么做。

请注意,php:// input不适用于指定Content-Type: multipart/form-data标头的请求(enctype="multipart/form-data"HTML格式)。这是由于PHP已经将表单数据解析为$_POST超全局数据。


17
请注意,AFAICS,STDIN流不可运行PHP使用CGI的系统,即通过mod_fcgid或的mod_fastcgi等
SCY

但是,我随请求传递变量(作为表单数据),我如何访问指定值,我随请求将Grant_type = password&username = user&password = pass作为表单数据主体传递,我将如何从“ $实体”
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")它将起作用。
Sinus Mackowaty

16

返回数组中的值

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

在这种情况下,您现在必须遍历$data关联数组,以检查每个值是否以所需的方式编码。观察事物的“流到数据类型”方式可能很简单,但可能不如使用流过滤器处理“流形式”的编码有效。如果您不处理编码问题,而只是进行消毒和验证,那么您将丢失一个步骤。
Anthony Rutledge,

13

可能为空的原因$_POST是该请求不是POSTPOST不再存在...它可能已经以post开头,但是在某处遇到301302重定向,该请求已切换为GET

检查$_SERVER['REQUEST_METHOD']是否是这种情况。

请参阅https://stackoverflow.com/a/19422232/109787,以获取有关为什么不应该发生但仍然会发生这种情况的很好的讨论。


1
在开发REST api平台的上下文中提出了这个问题。
Itay Moav -Malimovka '16

我不确定你是什么意思?REST API也可能会在其路径中找到重定向,这就是我遇到的问题。
Legolas's

我的意思是,当我问这个问题时,我并不是在尝试解决错误,而是试图弄清楚如何开发它。
Itay Moav -Malimovka's

3
这个提示挽救了我的一天。接收服务器已重新配置为重定向到https,Whitch破坏了一些API客户端。
DesertEagle's

其实我的要求是,POST但经检查后证明是GET/在网址末尾添加a 后,它开始显示POST。奇怪的!
zackygaurav



2

如果安装了pecl / http扩展名,则也可以使用以下命令:

$request = new http\Env\Request();
$request->getBody();

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());

缺少此逻辑的地方是对Content-Type标头中找到的值进行测试。不能仅仅因为$ _POST为空就必须提交JSON,或者如果json_last_error() == JSON_ERROR_NONEfalse,就不能返回空数组。如果有人提交了XML或YAML怎么办?添加一个针对Content-Type的测试,然后从那里开始。
安东尼·鲁特里奇

同样,HTTP请求方法可以确定您是否要接受输入数据。有关$_SERVER有用的值,请参见superglobal。
安东尼·鲁特里奇

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.