关系梳理,浅谈PHP前后端传参常见的几种方式

在Web开发里面,有前后端之分,它们之间的交互主要通过传参的方式,但是这个传参也分几种形式,比如说Form表单提交、Ajax提交...今天我就在这里总结一下开发中常见的几种形式:

本文主要参考

  • 深入剖析PHP输入流 php://input

 

angular使用post、get向后台传参的问题

一、问题的来源

  我们都知道向后台传参可以使用get、put,其形式就类似于name=jyy&id=001。但是在ng中我却发现使用$http post进行异步传输的过程中后台是接收不到数据的。其实这个问题是因为请求头的缘故。在ng中默认的请求头是:“Content-Type":"application/json",也就是说传递参数是使用的就是json格式。但是后台默认的却是Content-Type': 'application/x-www-form-urlencoded'。因此在这样的情况下后台接收到的数据就会是空的。

  那么为什么使用get形式就可以传参呢?在书中我发现这样的一句话:”这个键的值是一个字符串map或对象,会被转换成查询字符串追加在URL后面。如果值不是字符串,会被JSON序列化”,可以理解为在get中参数的传递是直接追加在url后面的,那么此时参数形式{"id":"1","name":"jyy"}会被转化成id=1&name=jyy追加在url后面。那么在后台中就可以直接获取到了。例如:

图片 1

图片 2

图片 3

图片 4

var app = angular.module('app',[]);
        app.controller('ctrl',function($scope,$q,$http){
            var defer = $q.defer();
            var promise = defer.promise;
            $http({
                method: "get",
                params:{id:1,name:jyy},
                url:"1.php"
            }).success(function(data){
                defer.resolve(data);
            });
            promise.then(function(data){
                $scope.data = data;
            })
        })

图片 5

图片 6

图片 7

图片 8

  在后台(PHP)输入echo $_GET[id]就可以正常显示了。

那么接下来就研究怎么解决post的传值了。

二、问题的解决

  1. 修改请求头

  第一种方法就是在ng中修改请求头将json格式改成x-www-form-urlencoded。修改方法点击即可查看。

  值得注意的是,在使用第二种方法时,可以修改put,get,post,common的传参格式。因此修改哪种方式,就只能使用这种方式才能在后台得到参数。这篇博文写到使用common进行设置可以同时使用put、get、post进行传参。但是在我的实际操作中发现对common进行修改并不能使用post进行传参,而只有设置了post的请求头才可以。

  另外由于在ng中的参数都是使用json格式表达,因此需要引入jquery,使用其$.param("参数列表的json格式")进行序列化表示。

  首先使用修改post请求头。具体代码如下:

图片 9

图片 10

图片 11

图片 12

var app = angular.module('app');   
        app.config(function($httpProvider){
            $httpProvider.defaults.headers.post = { 'Content-Type': 'application/x-www-form-urlencoded' }
        })
        app.controller('ctrl',function($scope,$resource,$q,$http){
            var defer = $q.defer();
            var promise = defer.promise;
            $http({
                method: "post",
                data:$.param({"id":"1","name":"jyy"}),
                url:"1.php"
            }).success(function(data){
                defer.resolve(data);
            });
            promise.then(function(data){
                $scope.data = data;
            })
        })

图片 13

图片 14

图片 15

图片 16

  此时在后台中(使用的是php),输入echo $_POST[id],就会显示。而debug其中显示的请求头正是我们设置的。

  2.在后台进行解决

  由于使用的是php,所以暂时用php的方法解决。在这个方法中,我们不修改请求头。因为get传值是正常的,那么,只要能够得到post的值就好。既然ng向后台传值了,即便是因为请求头不同,但是总会传过来什么的吧,既然能够传过来,就先获取它。这个时候使用$GLOBALS['HTTP_RAW_POST_DATA']将这个传过来的东西显示出来。那么这个$GLOBALS['HTTP_RAW_POST_DATA']是什么呢?在网上查询得知$HTTP_RAW_POST_DATA 变量包含有原始的 POST 数据。此变量仅在碰到未识别 MIME 类型的数据时产生,PHP不能识别的Content-Type类型的时候,会将http请求包中相应的数据填入变量$HTTP_RAW_POST_DATA。就是说现在这个请求头虽然是有冲突的,但是却能够显示出来。如下:

图片 17

图片 18

图片 19

图片 20

var app = angular.module('app',[]);
        app.controller('ctrl',function($scope,$q,$http){
            var defer = $q.defer();
            var promise = defer.promise;
            $http({
                method: "post",
                data:{'id':'1','name':'jyy'},
                url:"1.php"
            }).success(function(data){
                defer.resolve(data);
            });
            promise.then(function(data){
                $scope.data = data;
            })
        })

图片 21

图片 22

图片 23

图片 24

php代码:

echo $GLOBALS['HTTP_RAW_POST_DATA'];

  此时显示出来的东西是:{"id":"1","name":"jyy"}。发现这个结果是正确显示了。那么直接对齐进行操作不就可以了?好吧,那就先看看是什么类型:使用gettype()得到的是string,就是说他是个json字符串。那就使用json_decode()转换,发现会报错。好吧,放弃使用这个方法。

  但是此时还有另外的方法。使用file_get_contents("php://input"),这个方法中 php://input 是个可以访问请求的原始数据的只读流。 POST 请求的情况下,最好使用 php://input 来代替 $HTTP_RAW_POST_DATA,因为它不依赖于特定的 php.ini 指令。 此时这个方法就可以返回传过来的参数了。代码如下:

$a = json_decode(file_get_contents("php://input"));
    echo $a->id;

  结果输出正确的id。

转载自:

angular使用post、get向后台传参的问题

1. Form表单提交

这种方式是最原始最常见的方式,提交的时候也有可能是通过js触发,其请求头Content-Type为: application/x-www-form-urlencoded,示例如下:

最近在Coding的时候遇到了一个让人头晕的问题:前端用axios库提交的数据,在后端框架无法捕获;于是本着追本溯源的精神开始了这个问题的探索!

一、问题的来源

前端代码:
<body> <form action="backend.php" method="post"> <label for="name">姓名:</label> <input type="text" name="name"> <label for="name">年龄:</label> <input type="text" name="age"> <input type="submit" value="提交"> </form></body>
一、填坑

图片 25

前端数据抓包

首先抓取前端的数据请求包,可以看到HTTP Method为PUT,提交的数据在entity body中,并且为JSON数据格式,但是我们在后台接口框架中打印捕获到的POST数据为:

图片 26

image.png

很显然后端框架虽然捕获到了前端提交的POST数据,但是并没有正确解析,因此问题应该主要出在框架上(后端使用Phalcon框架)。通过跟踪代码看到,后端获取POST数据的代码为:

   if ($method == PostedDataMethods::JSON_BODY) {
        return $this->getJsonRawBody(true);
    }
    else if($method == PostedDataMethods::POST) {
        return $this->getPost();
    }
    else if($method == PostedDataMethods::PUT) {
        return $this->getPut();
    }
    else if($method == PostedDataMethods::GET) {
        return $this->getQuery();
    }

接口框架通过判断HTTP请求方法调用不同的方法获取POST数据,本例使用PUT提交数据,所以调用了getPut(),该方法为Phalcon框架的方法,因此调出Phalcon源码继续追踪。

Phalcon源码部分:

public function getPut(string! name = null, var filters = null, var defaultValue = null, boolean notAllowEmpty = false, boolean noRecursive = false) -> var
{
    var put;

    let put = this->_putCache;

    if typeof put != "array" {
        let put = [];
        parse_str(this->getRawBody(), put);

        let this->_putCache = put;
    }

    return this->getHelper(put, name, filters, defaultValue, notAllowEmpty, noRecursive);
}

/**
 * Gets HTTP raw request body
 */
public function getRawBody() -> string
{
    var rawBody, contents;

    let rawBody = this->_rawBody;
    if empty rawBody {

        let contents = file_get_contents("php://input");

        /**
         * We need store the read raw body because it can't be read again
         */
        let this->_rawBody = contents;
        return contents;
    }
    return rawBody;
}

从上面代码可以看到,对于PUT请求,Phalcon框架首先获取php://input输入流中的信息,在本例中,php://input输入流中的值为:

{"id":22,"package_name":"test","md5":"ste1","type":"3","op_id":0,"status":1,"update_time":1509677637,"create_time":1509625133}

因此该字符串通过parse_str()解析后,就变成了:

{{"id":22,"package_name":"test","md5":"ste1","type":"3","op_id":0,"status":1,"update_time":1509677637,"create_time":1509625133} : ""}

即:我们在后台获取到的POST值。
至此,这个问题的原因基本可以确定,是由于前端提交的数据是JSON格式的数据,然而Phalcon并没有去解析这个JSON数据,而是直接把它当做一个字符串,然后直接进行解析。

所以解决当前问题的解决方法有:

  • 让前端提交的数据格式由JSON格式转化为形如:id=22&package_name=test&…… 的字符串形式,从而后端能够正确解析前端传过来的参数;
  • 或者在接口框架中指定前端传过来的数据为JSON格式,让接口框架调用Phalcon框架中getJsonRawBody()方法,从而也能够正确解析前端传过来的参数

最后,实践证明这两种方法都有效!

  我们都知道向后台传参可以使用get、put,其形式就类似于name=jyy&id=001。但是在ng中我却发现使用$http post进行异步传输的过程中后台是接收不到数据的。其实这个问题是因为请求头的缘故。在ng中默认的请求头是:“Content-Type":"application/json",也就是说传递参数是使用的就是json格式。但是后台默认的却是Content-Type': 'application/x-www-form-urlencoded'。因此在这样的情况下后台接收到的数据就会是空的。

后端接收:
<?phpvar_dump($_POST['name']);var_dump($_GET['age']);var_dump($_REQUEST['age]);
二、扩展

这个问题虽然解决了,但是自己仍然对不同HTTP method情况下,http请求参数获取的内部关系一知半解,在仔细阅读了Phalcon 请求参数获取部分源码后,我知道自己需要先从PHP中php://input输入流、$_POST、$_GET、$HTTP_RAW_POST_DATA这几个兄弟之间的关系开始了解。

  那么为什么使用get形式就可以传参呢?在书中我发现这样的一句话:”这个键的值是一个字符串map或对象,会被转换成查询字符串追加在URL后面。如果值不是字符串,会被JSON序列化”,可以理解为在get中参数的传递是直接追加在url后面的,那么此时参数形式{"id":"1","name":"jyy"}会被转化成id=1&name=jyy追加在url后面。那么在后台中就可以直接获取到了。例如:

请求头:

图片 27

这种提交方式也是ajax默认的提交方式,请求参数是以key-value键值对的形式传递到后端,在PHP里面通$_POST等超全局变量就可以获取到,简单实用。其未经解析的原始的数据其实是:name=PHP&age=25

图片 28

2.1 php://input

php://input是个可以访问请求的原始数据的只读流。 POST请求的情况下,最好使用php://input来代替$HTTP_RAW_POST_DATA,因为它不依赖于特定的php.ini指令。 而且,这样的情况下$HTTP_RAW_POST_DATA默认没有填充, 比激活always_populate_raw_post_data潜在需要更少的内存。当enctype="multipart/form-data"的时候,php://input是无效的。

  • 不需要任何特殊的 php.ini 设置
  • 不能用于 enctype="multipart/form-data"

图片 29

2. JSON形式提交

这种形式,需要设置一下请求头Content-Type为application/json,实例如下:

2.2 $_POST

$_POST是我们最常用的获取POST数据的方式,它是以关联数组方式组织提交的数据,并对此进行编码处理,如urldecode,甚至编码转换,识别的数据类型是PHP默认识别的数据类型 application/x-www.form-urlencoded

  • 无法解析如text/xml,application/json等非 application/x-www.form-urlencoded 数据类型的内容
var app = angular.module('app',[]);
        app.controller('ctrl',function($scope,$q,$http){
            var defer = $q.defer();
            var promise = defer.promise;
            $http({
                method: "get",
                params:{id:1,name:jyy},
                url:"1.php"
            }).success(function(data){
                defer.resolve(data);
            });
            promise.then(function(data){
                $scope.data = data;
            })
        })
前端代码:
 $.ajax({ type: 'POST', url: "backend.php", data: { 'name': 'hello', 'age': 15, }, contentType: 'application/json', dataType: "json", success: function  { console.log; } });
2.3 $HTTP_RAW_POST_DATA

PHP默认识别的数据类型是application/x-www.form-urlencoded,用Content-Type=application/json 类型,提交的POST数据这时候 $_POST 就无法获取到了,但是使用 $GLOBALS['HTTP_RAW_POST_DATA'] 可以获取到。因为在PHP无法识别Content-Type的时候,就会把 POST 数据填入到 $HTTP_RAW_POST_DATA 中。

  • 需要设置 php.ini 中的 always_populate_raw_post_data 值为 on 才会生效
  • 当$_POST 与 php://input可以取到值时 $HTTP_RAW_POST_DATA 为空
  • 不能用于 enctype="multipart/form-data"
  • PHP7中已经移除了这个全局变量,用 php://input 替代

图片 30

请求头:

图片 31

从上面的截图可以看到,请求参数那里变成Request Payload,虽然格式上看上去和之前form提交差不多,但是这时候如果后台用$_POST这类方法是无法获取的,需要换一种方式:

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

上面这种方式获取到的内容是字符串: name=Jun&age=15,在这个例子里面反而不容易处理了,实际上采用json这种方式提交的参数的话,一般都是把需要的数据封装成json格式提交,在js里面就是把数据放到对象里面,然后序列化:

 var data = { 'name': 'Jun', 'age': 15, }; $.ajax({ type: 'POST', url: "backend.php", data: JSON.stringify, contentType: 'application/json', dataType: "json", success: function  { console.log; } });

这是再查看请求头:

图片 32

可以看到参数变成json格式,这时候PHP后端就可以采用json_decode函数去获取参数:

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

从带有 GET 方法的表单发送的信息,对任何人都是可见的(会显示在浏览器的地址栏),并且对发送信息的量也有限制。

  在后台(PHP)输入echo $_GET[id]就可以正常显示了。

3.文件上传

一般上传图片等各种文件的时候用的到,Content-Type是 multipart/form-data

请求头类似如下:

------WebKitFormBoundary63FiWN3UoYxd8OT6Content-Disposition: form-data; name="UploadFile"; filename="QQ截图20170925101502.png"Content-Type: image/png------WebKitFormBoundary63FiWN3UoYxd8OT6Content-Disposition: form-data; name="sid"sid------WebKitFormBoundary63FiWN3UoYxd8OT6Content-Disposition: form-data; name="fun"add------WebKitFormBoundary63FiWN3UoYxd8OT6Content-Disposition: form-data; name="mode"
三、总结

那么接下来就研究怎么解决post的传值了。

4. 总结

这几种方式功能上说没什么区别,都能实现数据的提交,大家选择自己喜欢的方式就行,最重要的是前后端协调好, 虽然这里后端是以PHP为例,但是其他语言也是大同小异。最后,再说一下数组提交,这个倒不是新的提交方式,我这里是指遇到那种一个字段提交多个数据的情况,比如说删除多个文章,一般前端需要传多个id,举例子字段名字叫ids,一般有这2种方案:

3.1 php://input

php://input数据与http entity body部分数据是总是一致的(该部分相一致的数据的长度由Content-Length指定),除了当Content-Type为multipart/form-data的时候。

multipart/form-data表示以POST方法提交表单数据,它还伴随了文件上传,所以会跟application/x-www-form-urlencoded数据格式不一样。它会以一更种更合理的,更高效的数据格式传递给服务端。

二、问题的解决

1. 逗号相隔

图片 33

这样传参,后端获取到之后是一个字符串,在PHP里面可以用explode这样的函数去把字符串拆分成数组,非常方便,当然你也可以选择其他分隔符,比如说“-”,“ ”等字符。

3.2 $_POST

只有在Content-Type为application/x-www-form-urlencoded或者为multipart/form-data的时候,PHP才会将http请求数据包中的body相应部分数据填入$_POST全局变量中,其它情况PHP都忽略。

  1. 修改请求头

2. JSON形式

这就是文中说的第二种方式,把id放在数组里面以json方式传到后台,这样后台可以直接获取到一个数组.

图片 34

3.3 $HTTP_RAW_POST_DATA

$HTTP_RAW_POST_DATA数据总是跟php://input相同,但是php://input比$HTTP_RAW_POST_DATA更高效,且不需要特殊设置php.ini。

  第一种方法就是在ng中修改请求头将json格式改成x-www-form-urlencoded。修改方法点击即可查看。

3.4 $_GET

PHP会将PATH字段的query_path部分,填入全局变量$_GET。通常情况下,GET方法提交的http请求body为空。

注:文中如有任何错误,请各位批评指正!

  值得注意的是,在使用第二种方法时,可以修改put,get,post,common的传参格式。因此修改哪种方式,就只能使用这种方式才能在后台得到参数。这篇博文写到使用common进行设置可以同时使用put、get、post进行传参。但是在我的实际操作中发现对common进行修改并不能使用post进行传参,而只有设置了post的请求头才可以。

  另外由于在ng中的参数都是使用json格式表达,因此需要引入jquery,使用其$.param("参数列表的json格式")进行序列化表示。

  首先使用修改post请求头。具体代码如下:

图片 35

var app = angular.module('app');
  
        app.config(function($httpProvider){
            $httpProvider.defaults.headers.post = { 'Content-Type': 'application/x-www-form-urlencoded' }
        })
        app.controller('ctrl',function($scope,$resource,$q,$http){
            var defer = $q.defer();
            var promise = defer.promise;
            $http({
                method: "post",
                data:$.param({"id":"1","name":"jyy"}),
                url:"1.php"
            }).success(function(data){
                defer.resolve(data);
            });
            promise.then(function(data){
                $scope.data = data;
            })
        })

图片 36

  此时在后台中(使用的是php),输入echo $_POST[id],就会显示。而debug其中显示的请求头正是我们设置的。

  2.在后台进行解决

  由于使用的是php,所以暂时用php的方法解决。在这个方法中,我们不修改请求头。因为get传值是正常的,那么,只要能够得到post的值就好。既然ng向后台传值了,即便是因为请求头不同,但是总会传过来什么的吧,既然能够传过来,就先获取它。这个时候使用$GLOBALS['HTTP_RAW_POST_DATA']将这个传过来的东西显示出来。那么这个$GLOBALS['HTTP_RAW_POST_DATA']是什么呢?在网上查询得知$HTTP_RAW_POST_DATA 变量包含有原始的 POST 数据。此变量仅在碰到未识别 MIME 类型的数据时产生,PHP不能识别的Content-Type类型的时候,会将http请求包中相应的数据填入变量$HTTP_RAW_POST_DATA。就是说现在这个请求头虽然是有冲突的,但是却能够显示出来。如下:

图片 37

var app = angular.module('app',[]);
        app.controller('ctrl',function($scope,$q,$http){
            var defer = $q.defer();
            var promise = defer.promise;
            $http({
                method: "post",
                data:{'id':'1','name':'jyy'},
                url:"1.php"
            }).success(function(data){
                defer.resolve(data);
            });
            promise.then(function(data){
                $scope.data = data;
            })
        })

图片 38

php代码:

echo $GLOBALS['HTTP_RAW_POST_DATA'];

  此时显示出来的东西是:{"id":"1","name":"jyy"}。发现这个结果是正确显示了。那么直接对齐进行操作不就可以了?好吧,那就先看看是什么类型:使用gettype()得到的是string,就是说他是个json字符串。那就使用json_decode()转换,发现会报错。好吧,放弃使用这个方法。

  但是此时还有另外的方法。使用file_get_contents("php://input"),这个方法中 php://input 是个可以访问请求的原始数据的只读流。 POST 请求的情况下,最好使用 php://input 来代替 $HTTP_RAW_POST_DATA,因为它不依赖于特定的 php.ini 指令。 此时这个方法就可以返回传过来的参数了。代码如下:

$a = json_decode(file_get_contents("php://input"));
    echo $a->id;

  结果输出正确的id。

本文由星彩网app下载发布于计算机编程,转载请注明出处:关系梳理,浅谈PHP前后端传参常见的几种方式

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。