我已经被指示使用方法php://input而不是$_POST时,与Ajax请求从JQuery交互。我不明白的是使用这个与$_POST或$_GET的全局方法的好处。
php://input可以给你数据的原始字节。如果POST数据是JSON编码的结构,这是很有用的,AJAX POST请求通常是这种情况。
下面是一个函数:
/**
* Returns the JSON encoded POST data, if any, as an object.
*
* @return Object|null
*/
private function retrieveJsonPostData()
{
// get the raw POST data
$rawData = file_get_contents("php://input");
// this returns null if not valid json
return json_decode($rawData);
}
当处理表单中由传统POST提交的键值数据时,$_POST数组更有用。只有当post的数据是一种可识别的格式时,这才有效,通常是application/x-www-form-urlencoded(详情请参阅http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4)。
如果post数据格式不正确,$_POST将不包含任何内容。然而,php://input将会有畸形的字符串。
例如,有一些ajax应用程序,不形成正确的post键值序列上传文件,只是转储所有的文件作为post数据,没有变量名或任何东西。 $_POST为空,$_FILES也为空,php://input将包含精确的文件,写为字符串。
原因是php://input返回请求的HTTP-headers之后的所有原始数据,而不管内容类型是什么。
PHP的超全局变量$_POST,只能包装任意一种类型的数据
Application /x-www-form-urlencoded(简单表单帖子的标准内容类型)或 Multipart /form-data(主要用于文件上传)
这是因为这些是用户代理必须支持的唯一内容类型。因此,服务器和PHP传统上不期望接收任何其他内容类型(这并不意味着它们不能)。
所以,如果你只是简单地POST一个好的旧HTML表单,请求看起来像这样:
POST /page.php HTTP/1.1
key1=value1&key2=value2&key3=value3
但如果您经常使用Ajax,这可能还包括用类型(字符串、int、bool)和结构(数组、对象)交换更复杂的数据,因此在大多数情况下JSON是最佳选择。但是带有json有效负载的请求应该是这样的:
POST /page.php HTTP/1.1
{"key1":"value1","key2":"value2","key3":"value3"}
内容现在是application/json(或者至少不是上面提到的任何一个),所以PHP的$ _post包装器还不知道如何处理它。
数据仍然在那里,只是不能通过包装器访问它。所以你需要自己用file_get_contents('php://input')获取原始格式(只要它不是multipart/form-data-encoded)。
这也是访问xml数据或任何其他非标准内容类型的方法。
简单的例子如何使用它
<?php
if(!isset($_POST) || empty($_POST)) {
?>
<form name="form1" method="post" action="">
<input type="text" name="textfield"><br />
<input type="submit" name="Submit" value="submit">
</form>
<?php
} else {
$example = file_get_contents("php://input");
echo $example; }
?>
首先,关于PHP的一个基本事实。
PHP的设计初衷并不是明确地为您提供一个纯REST (GET, POST, PUT, PATCH, DELETE)类的接口来处理HTTP请求。
但是,$_SERVER、$_COOKIE、$ post、$_GET和$_FILES超全局变量以及函数filter_input_array()对于普通人/外行的需求非常有用。
$_POST(和$_GET)最大的隐藏优势是PHP会自动对输入数据进行url解码。您甚至从来没有想过必须这样做,特别是对于标准GET请求中的查询字符串参数,或与POST请求一起提交的HTTP主体数据。
其他HTTP请求方法
那些研究底层HTTP协议及其各种请求方法的人会了解到有许多HTTP请求方法,包括经常引用的PUT、PATCH(在谷歌的Apigee中没有使用)和DELETE。
在PHP中,当不使用POST时,没有用于获取HTTP请求主体数据的超全局函数或输入过滤器函数。罗伊·菲尔丁的弟子们要做什么?: -)
然而,你会学到更多……
话虽如此,随着PHP编程知识的提高,并希望使用JavaScript的XmlHttpRequest对象(对于某些人来说是jQuery),您就会看到这种方案的局限性。
$_POST限制你在HTTP Content-Type报头中使用两种媒体类型:
应用程序/ x-www-form-urlencoded, 多部分/格式
因此,如果你想在服务器端向PHP发送数据值,并让它显示在$_POST超全局变量中,那么你必须在客户端对它进行urlencode,并将数据作为键/值对发送——这对于新手来说是一个不方便的步骤(特别是在试图弄清楚URL的不同部分是否需要不同形式的urlencoding: normal, raw等等)。
对于所有jQuery用户,$.ajax()方法在将JSON传输到服务器之前将它们转换为URL编码的键/值对。你可以通过设置processData: false来覆盖这个行为。只需阅读$.ajax()文档,不要忘记在Content-Type报头中发送正确的媒体类型。
php://input, but…
即使你使用php://input而不是$_POST作为你的HTTP POST请求体数据,它也不会与multipart/form-data的HTTP内容类型一起工作,这是你在HTML表单上使用的内容类型,当你想要允许文件上传!
<form enctype="multipart/form-data" accept-charset="utf-8" action="post">
<input type="file" name="resume">
</form>
因此,在传统的PHP中,要处理来自HTTP POST请求的各种内容类型,您将学习使用$_POST或filter_input_array(POST)、$_FILES和PHP://input。在PHP中,不可能只使用一个通用的HTTP POST请求输入源。
你不能通过$_POST, filter_input_array(POST)或php://input获取文件,也不能通过filter_input_array(POST)或$_POST获取JSON/XML/YAML。
PHP手册:PHP://输入
input是一个只读流,允许你读取原始数据 从请求体…php://input不可用 enctype = " multipart /格式”。
PHP框架拯救?
像Codeigniter 4和Laravel这样的PHP框架使用facade来为上述对象提供一个更清晰的接口(IncomingRequest或Request对象)。这就是为什么专业的PHP开发人员使用框架而不是原始PHP。
当然,如果您喜欢编程,您可以设计自己的facade对象来提供框架的功能。正是因为我花了时间来研究这个问题,我才能够写下这个答案。
URL编码吗?搞什么鬼!!???
通常情况下,如果你正在对一个HTML表单进行正常的、同步的(当整个页面重新绘制时)HTTP请求,用户代理(web浏览器)将为你对表单数据进行urlencode。如果你想使用XmlHttpRequest对象进行异步HTTP请求,那么如果你想让数据显示在$_POST超全局变量中,你必须创建一个urlencoded字符串并发送它。
你和JavaScript有多熟?:-)
从JavaScript数组或对象到urlencoded字符串的转换困扰着许多开发人员(即使是像Form Data这样的新api)。他们更希望能够发送JSON,而且这样做对客户端代码来说会更有效。
记住(眨眼,眨眼),一般的web开发人员不会像你我一样学会直接使用XmlHttpRequest对象、全局函数、字符串函数、数组函数和正则表达式;-)。Urlencoding对他们来说是一场噩梦。: -)
PHP给出了什么?
PHP缺乏直观的XML和JSON处理,这让很多人望而却步。您可能认为它现在已经是PHP的一部分了(唉)。
如此多的媒体类型(过去的MIME类型)
XML、JSON和YAML都有可以放入HTTP Content-Type报头中的媒体类型。
应用程序/ xml 应用/ json application/yaml(虽然IANA没有列出官方名称)
看看IANA定义了多少媒体类型(以前是MIME类型)。
看看有多少HTTP头。
php://input或bust
使用php://输入流允许您绕过php强加给世界的保姆/手把手级别的抽象。:-)能力越大责任越大!
现在,在处理通过php://input传输的数据值之前,您应该/必须做几件事。
确定是否指示了正确的HTTP方法(GET、POST、 放置,修补,删除,…) 确定是否传输了HTTP内容类型报头。 确定Content-Type的值是否是所需的媒体 类型。 确定发送的数据是否是格式良好的XML / JSON / YAML / 等。 如果需要,将数据转换为PHP数据类型:数组或 对象。 如果这些基本检查或转换失败,则抛出异常!
字符编码呢?
啊,哈哈!是的,您可能希望发送到应用程序中的数据流是UTF-8编码的,但是如何知道它是否是UTF-8编码的呢?
两个关键问题。
你不知道有多少数据通过php://input输出。 您不能确定数据流的当前编码。
您是否打算在不知道流数据有多少的情况下处理流数据?这是个糟糕的主意。您不能完全依赖HTTP Content-Length头来指导流输入的大小,因为它可能被欺骗。
你需要一个:
流大小检测算法。 应用程序定义的流大小限制(Apache / Nginx / PHP限制可能太宽泛)。
您是否打算在不知道流的当前编码的情况下尝试将流数据转换为UTF-8 ?如何?iconv流过滤器(iconv流过滤器的例子)似乎需要一个开始和结束编码,像这样。
'convert.iconv.ISO-8859-1/UTF-8'
因此,如果你是认真的,你需要:
流编码检测算法。 动态/运行时流过滤器定义算法(因为你无法先验地知道起始编码)。
(更新:“convert.iconv。UTF-8/UTF-8'将强制所有内容为UTF-8,但您仍然必须考虑到iconv库可能不知道如何翻译的字符。换句话说,你必须在某种程度上定义当字符无法翻译时采取的操作:1)插入一个虚拟字符,2)失败/抛出和异常)。
您不能完全依赖HTTP Content-Encoding报头,因为这可能表示如下所示的压缩。关于iconv,这不是你想做的决定。
Content-Encoding: gzip
因此,一般的步骤可能是……
第一部分:HTTP请求相关
确定是否指示了正确的HTTP方法(GET、POST、 放置,修补,删除,…) 确定是否传输了HTTP内容类型报头。 确定Content-Type的值是否是所需的媒体 类型。
第二部分:流数据相关
确定输入流的大小(可选,但推荐)。 确定输入流的编码。 如果需要,将输入流转换为所需的字符 编码(utf - 8)。 如有必要,反转任何应用程序级别的压缩或加密,然后重复步骤4、5和6。
第三部分:数据类型相关
判断发送的数据是否是格式良好的XML / JSON / YMAL / 等。
(请记住,数据仍然可以是URL编码的字符串,然后必须解析和URL解码)。
如果需要,将数据转换为PHP数据类型:数组或 对象。
第四部分:数据价值相关
筛选输入数据。 验证输入数据。
现在你明白了吗?
$_POST超全局变量以及php.ini对输入限制的设置对于外行来说更简单。然而,当使用流时,处理字符编码更加直观和有效,因为不需要通过超全局变量(通常是数组)来检查输入值是否有正确的编码。
if (strtoupper($_SERVER['REQUEST_METHOD']) != 'POST') {
throw new Exception('Only POST requests are allowed');
}
// Make sure Content-Type is application/json
$content_type = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '';
if (stripos($content_type, 'application/json') === false) {
throw new Exception('Content-Type must be application/json');
}
// Read the input stream
$body = file_get_contents("php://input");
// Decode the JSON object
$object = json_decode($body, true);
推荐文章
- 编写器更新和安装之间有什么区别?
- 我如何捕捉Ajax查询后错误?
- 本地机器上的PHP服务器?
- 如何评论laravel .env文件?
- 在PHP中检测移动设备的最简单方法
- 如何在树枝模板中呈现DateTime对象
- 如何删除查询字符串,只得到URL?
- 如何检测URL是否在JavaScript哈希后发生了变化
- 您是否可以“编译”PHP代码并上传一个二进制文件,该文件将由字节码解释器运行?
- 非法字符串偏移警告PHP
- 从数组中获取随机项
- 在输入数字中隐藏上下箭头按钮(旋转器)- Firefox 29
- 为什么一个函数检查字符串是否为空总是返回true?
- 如何使用Laravel迁移将时间戳列的默认值设置为当前时间戳?
- 如何增加php的最大执行时间