xiaoshenzhen 1天前 67次浏览 1条回复 0 0 0

Fecshop原文地址:curl发送 JSON格式POST数据的接收,以及在yii2框架中的实现原理【精细剖析】

1.通过curl发送json格式的数据,譬如代码:

<?php
function http_post_json($url, $jsonStr)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonStr);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'Content-Type: application/json; charset=utf-8',
            'Content-Length: ' . strlen($jsonStr)
        )
    );
    $response = curl_exec($ch);
    //$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
 
    return $response;
}


$api_url = 'http://fecshop.appapi.fancyecommerce.com/44.php';
$post_data = [
    'username'     => 'terry',
    'password'     => 'terry4321'
];

然后在接收端,使用$_POST接收,发现打印是空的

原因是,PHP默认只识别application/x-www.form-urlencoded标准的数据类型,因此接收不到,只能通过

//第一种方法
$post = $GLOBALS['HTTP_RAW_POST_DATA'];
//第二种方法
$post = file_get_contents("php://input");

来接收

2.如果我们在Yii2框架内,想通过

$username       = Yii::$app->request->post('username');
$password       = Yii::$app->request->post('password');

这种方式获取第一部分使用curl json方式传递的post参数,我们发现是不行的,我们需要设置yii2 request component

'request' => [
            'class' => 'yii\web\Request',
            'parsers' => [
                 'application/json' => 'yii\web\JsonParser',
            ],
        ],

然后我们通过

$username       = Yii::$app->request->post('username');
$password       = Yii::$app->request->post('password');

发现是可以取值的了,然后如果你打印 $_POST,会发现这里依旧没有值,这是为什么呢?

下面我们通过代码顺藤摸瓜的查一下Yii2的源代码:

1.打开 yii\web\Request 找到post()方法:

public function post($name = null, $defaultValue = null)
    {
        if ($name === null) {
            return $this->getBodyParams();
        }

        return $this->getBodyParam($name, $defaultValue);
    }

发现值是由 $this->getBodyParam($name, $defaultValue) 给予

然后找到这个方法,代码如下:

/**
     * Returns the request parameters given in the request body.
     *
     * Request parameters are determined using the parsers configured in parsers property.
     * If no parsers are configured for the current contentType it uses the PHP function `mb_parse_str()`
     * to parse the rawBody.
     * @return array the request parameters given in the request body.
     * @throws \yii\base\InvalidConfigException if a registered parser does not implement the RequestParserInterface.
     * @see getMethod()
     * @see getBodyParam()
     * @see setBodyParams()
     */
    public function getBodyParams()
    {
        if ($this->_bodyParams === null) {
            if (isset($_POST[$this->methodParam])) {
                $this->_bodyParams = $_POST;
                unset($this->_bodyParams[$this->methodParam]);
                return $this->_bodyParams;
            }

            $rawContentType = $this->getContentType();
            if (($pos = strpos($rawContentType, ';')) !== false) {
                // e.g. application/json; charset=utf-8
                $contentType = substr($rawContentType, 0, $pos);
            } else {
                $contentType = $rawContentType;
            }

            if (isset($this->parsers[$contentType])) {
                $parser = Yii::createObject($this->parsers[$contentType]);
                if (!($parser instanceof RequestParserInterface)) {
                    throw new InvalidConfigException("The '$contentType' request parser is invalid. It must implement the yii\\web\\RequestParserInterface.");
                }
                $this->_bodyParams = $parser->parse($this->getRawBody(), $rawContentType);
            } elseif (isset($this->parsers['*'])) {
                $parser = Yii::createObject($this->parsers['*']);
                if (!($parser instanceof RequestParserInterface)) {
                    throw new InvalidConfigException("The fallback request parser is invalid. It must implement the yii\\web\\RequestParserInterface.");
                }
                $this->_bodyParams = $parser->parse($this->getRawBody(), $rawContentType);
            } elseif ($this->getMethod() === 'POST') {
                // PHP has already parsed the body so we have all params in $_POST
                $this->_bodyParams = $_POST;
            } else {
                $this->_bodyParams = [];
                mb_parse_str($this->getRawBody(), $this->_bodyParams);
            }
        }

        return $this->_bodyParams;
    }

打印 $rawContentType = $this->getContentType(); 这个变量,发现他的值为:
application/json , 然后查看函数getContentType()

public function getContentType()
    {
        if (isset($_SERVER['CONTENT_TYPE'])) {
            return $_SERVER['CONTENT_TYPE'];
        }

        if (isset($_SERVER['HTTP_CONTENT_TYPE'])) {
            //fix bug https://bugs.php.net/bug.php?id=66606
            return $_SERVER['HTTP_CONTENT_TYPE'];
        }

        return null;
    }

也就是 当我们发送json格式的curl请求, $_SERVER['CONTENT_TYPE'] 的值为 application/json

2.重新回到上面的函数 getBodyParams(),他会继续执行下面的代码:

			if (isset($this->parsers[$contentType])) {
                $parser = Yii::createObject($this->parsers[$contentType]);
                if (!($parser instanceof RequestParserInterface)) {
                    throw new InvalidConfigException("The '$contentType' request parser is invalid. It must implement the yii\\web\\RequestParserInterface.");
                }
                $this->_bodyParams = $parser->parse($this->getRawBody(), $rawContentType);
            }

$parser 就是根据我们下面的request component配置中的 parsers中得到'yii\web\JsonParser',进而通过容器生成出来的

'request' => [
            'class' => 'yii\web\Request',
            'enableCookieValidation' => false,
            'parsers' => [
                 'application/json' => 'yii\web\JsonParser',
            ],
        ],

因此返回值就是 $parser->parse($this->getRawBody(), $rawContentType); 返回的,

3.首先我们查看传递的第一个参数是函数 $this->getRawBody(),代码如下:

public function getRawBody()
    {
        if ($this->_rawBody === null) {
            $this->_rawBody = file_get_contents('php://input');
        }

        return $this->_rawBody;
    }

通过这个函数,回到前面我们说的,可以通过

//第一种方法
$post = $GLOBALS['HTTP_RAW_POST_DATA'];
//第二种方法
$post = file_get_contents("php://input");

这两种方式获取curl json传递的json数据,yii2使用的是第二种。

然后我们打开yii\web\JsonParser

/**
     * Parses a HTTP request body.
     * @param string $rawBody the raw HTTP request body.
     * @param string $contentType the content type specified for the request body.
     * @return array parameters parsed from the request body
     * @throws BadRequestHttpException if the body contains invalid json and throwException is `true`.
     */
    public function parse($rawBody, $contentType)
    {
        try {
            $parameters = Json::decode($rawBody, $this->asArray);
            return $parameters === null ? [] : $parameters;
        } catch (InvalidParamException $e) {
            if ($this->throwException) {
                throw new BadRequestHttpException('Invalid JSON data in request body: ' . $e->getMessage());
            }
            return [];
        }
    }

可以看到这里是将传递的json转换成数组,然后Yii::request->post('username')就可以从返回的这个数组中取值了

总结:

1.在Yii2框架中要用封装的post() 和 get()方法, 而不要使用$_POST $_GET等方法,因为两者是不相等的。

2.Yii2做api的时候,如果是json格式传递数据,一定不要忘记在request component中加上配置:

'request' => [
            'class' => 'yii\web\Request',
            'parsers' => [
                 'application/json' => 'yii\web\JsonParser',
            ],
        ],

本文由 Terry 创作,采用 知识共享署名 3.0 中国大陆许可协议 进行许可。
可自由转载、引用,但需署名作者且注明文章出处。

您需要登录后才可以回复。登录 | 立即注册