Commit 813a008d by Carsten Brandt

improved request body parser docs and configuration

added a fallback for handling all request types fixes #2066, related to #2043
parent 8a94eb1f
...@@ -4,7 +4,6 @@ Yii Framework 2 Change Log ...@@ -4,7 +4,6 @@ Yii Framework 2 Change Log
2.0.0 beta under development 2.0.0 beta under development
---------------------------- ----------------------------
- Enh #2043: Added support for custom web\Request parsers (danschmidt5189)
- Bug #1326: The `visible` setting for `DetailView` doesn't work as expected (qiangxue) - Bug #1326: The `visible` setting for `DetailView` doesn't work as expected (qiangxue)
- Bug #1446: Logging while logs are processed causes infinite loop (qiangxue) - Bug #1446: Logging while logs are processed causes infinite loop (qiangxue)
- Bug #1497: Localized view files are not correctly returned (mintao) - Bug #1497: Localized view files are not correctly returned (mintao)
...@@ -74,6 +73,7 @@ Yii Framework 2 Change Log ...@@ -74,6 +73,7 @@ Yii Framework 2 Change Log
- Enh #1973: `yii message/extract` is now able to generate `.po` files (SergeiKutanov, samdark) - Enh #1973: `yii message/extract` is now able to generate `.po` files (SergeiKutanov, samdark)
- Enh #1984: ActionFilter will now mark event as handled when action run is aborted (cebe) - Enh #1984: ActionFilter will now mark event as handled when action run is aborted (cebe)
- Enh #2003: Added `filter` property to `ExistValidator` and `UniqueValidator` to support adding additional filtering conditions (qiangxue) - Enh #2003: Added `filter` property to `ExistValidator` and `UniqueValidator` to support adding additional filtering conditions (qiangxue)
- Enh #2043: Added support for custom request body parsers (danschmidt5189, cebe)
- Enh #2051: Do not save null data into database when using RBAC (qiangxue) - Enh #2051: Do not save null data into database when using RBAC (qiangxue)
- Enh: Added `favicon.ico` and `robots.txt` to default application templates (samdark) - Enh: Added `favicon.ico` and `robots.txt` to default application templates (samdark)
- Enh: Added `Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue) - Enh: Added `Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue)
......
...@@ -11,7 +11,7 @@ use yii\base\InvalidParamException; ...@@ -11,7 +11,7 @@ use yii\base\InvalidParamException;
use yii\helpers\Json; use yii\helpers\Json;
/** /**
* Parses a raw HTTP request using Json::encode() * Parses a raw HTTP request using [[yii\helpers\Json::decode()]]
* *
* @author Dan Schmidt <danschmidt5189@gmail.com> * @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0 * @since 2.0
...@@ -22,23 +22,26 @@ class JsonParser implements RequestParserInterface ...@@ -22,23 +22,26 @@ class JsonParser implements RequestParserInterface
* @var boolean whether to return objects in terms of associative arrays. * @var boolean whether to return objects in terms of associative arrays.
*/ */
public $asArray = true; public $asArray = true;
/** /**
* @var boolean whether to throw an exception if the body is invalid json * @var boolean whether to throw a [[BadRequestHttpException]] if the body is invalid json
*/ */
public $throwException = false; public $throwException = true;
/** /**
* @param string $rawBody the raw HTTP request body * 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 * @return array parameters parsed from the request body
* @throws BadRequestHttpException if the body contains invalid json and [[throwException]] is `true`.
*/ */
public function parse($rawBody) public function parse($rawBody, $contentType)
{ {
try { try {
return Json::decode($rawBody, $this->asArray); return Json::decode($rawBody, $this->asArray);
} catch (InvalidParamException $e) { } catch (InvalidParamException $e) {
if ($this->throwException) { if ($this->throwException) {
throw $e; throw new BadRequestHttpException('Invalid JSON data in request body: ' . $e->getMessage(), 0, $e);
} }
return null; return null;
} }
......
...@@ -129,9 +129,22 @@ class Request extends \yii\base\Request ...@@ -129,9 +129,22 @@ class Request extends \yii\base\Request
*/ */
public $restVar = '_method'; public $restVar = '_method';
/** /**
* @var array the parsers for converting the raw request body into [[restParams]] * @var array the parsers for converting the raw HTTP request body into [[restParams]].
* The array keys are the request content-types, and the array values are the * The array keys are the request `Content-Types`, and the array values are the
* corresponding configurations for creating the parser objects. * corresponding configurations for [[Yii::createObject|creating the parser objects]].
* A parser must implement the [[RequestParserInterface]].
*
* To enable parsing for JSON requests you can use the [[JsonParser]] class like in the following example:
*
* ```
* [
* 'application/json' => 'yii\web\JsonParser',
* ]
* ```
*
* To register a parser for parsing all request types you can use `'*'` as the array key.
* This one will be used as a fallback in case no other types match.
*
* @see getRestParams() * @see getRestParams()
*/ */
public $parsers = []; public $parsers = [];
...@@ -256,20 +269,32 @@ class Request extends \yii\base\Request ...@@ -256,20 +269,32 @@ class Request extends \yii\base\Request
/** /**
* Returns the request parameters for the RESTful request. * Returns the request parameters for the RESTful request.
*
* 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|request body]].
* @return array the RESTful request parameters * @return array the RESTful request parameters
* @throws \yii\base\InvalidConfigException if a registered parser does not implement the [[RequestParserInterface]].
* @see getMethod() * @see getMethod()
*/ */
public function getRestParams() public function getRestParams()
{ {
if ($this->_restParams === null) { if ($this->_restParams === null) {
$contentType = $this->getContentType();
if (isset($_POST[$this->restVar])) { if (isset($_POST[$this->restVar])) {
$this->_restParams = $_POST; $this->_restParams = $_POST;
} else if (isset($this->parsers[$this->getContentType()])) { } elseif (isset($this->parsers[$contentType])) {
$parser = Yii::createObject($this->parsers[$this->getContentType()]); $parser = Yii::createObject($this->parsers[$contentType]);
if (! $parser instanceof RequestParserInterface) { if (!($parser instanceof RequestParserInterface)) {
throw new InvalidConfigException("The '{$this->contentType}' request parser is invalid. It must implement the RequestParserInterface."); throw new InvalidConfigException("The '$contentType' request parser is invalid. It must implement the yii\\web\\RequestParserInterface.");
} }
$this->_restParams = $parser->parse($this->getRawBody()); $this->_restParams = $parser->parse($this->getRawBody(), $contentType);
} 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->_restParams = $parser->parse($this->getRawBody(), $contentType);
} else { } else {
$this->_restParams = []; $this->_restParams = [];
mb_parse_str($this->getRawBody(), $this->_restParams); mb_parse_str($this->getRawBody(), $this->_restParams);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
namespace yii\web; namespace yii\web;
/** /**
* Interface for objects that parse the raw request body into a parameters array * Interface for classes that parse the raw request body into a parameters array.
* *
* @author Dan Schmidt <danschmidt5189@gmail.com> * @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0 * @since 2.0
...@@ -16,8 +16,10 @@ namespace yii\web; ...@@ -16,8 +16,10 @@ namespace yii\web;
interface RequestParserInterface interface RequestParserInterface
{ {
/** /**
* @param string $rawBody the raw HTTP request body * 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 * @return array parameters parsed from the request body
*/ */
public function parse($rawBody); public function parse($rawBody, $contentType);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment