| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- <?php
- /**
- * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
- * @link http://cakephp.org CakePHP(tm) Project
- * @since 3.6.0
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
- */
- namespace Cake\Http\Middleware;
- use Cake\Http\Exception\BadRequestException;
- use Cake\Utility\Exception\XmlException;
- use Cake\Utility\Xml;
- use Psr\Http\Message\ResponseInterface;
- use Psr\Http\Message\ServerRequestInterface;
- /**
- * Parse encoded request body data.
- *
- * Enables JSON and XML request payloads to be parsed into the request's
- * Provides CSRF protection & validation.
- *
- * You can also add your own request body parsers using the `addParser()` method.
- */
- class BodyParserMiddleware
- {
- /**
- * Registered Parsers
- *
- * @var array
- */
- protected $parsers = [];
- /**
- * The HTTP methods to parse data on.
- *
- * @var array
- */
- protected $methods = ['PUT', 'POST', 'PATCH', 'DELETE'];
- /**
- * Constructor
- *
- * ### Options
- *
- * - `json` Set to false to disable json body parsing.
- * - `xml` Set to true to enable XML parsing. Defaults to false, as XML
- * handling requires more care than JSON does.
- * - `methods` The HTTP methods to parse on. Defaults to PUT, POST, PATCH DELETE.
- *
- * @param array $options The options to use. See above.
- */
- public function __construct(array $options = [])
- {
- $options += ['json' => true, 'xml' => false, 'methods' => null];
- if ($options['json']) {
- $this->addParser(
- ['application/json', 'text/json'],
- [$this, 'decodeJson']
- );
- }
- if ($options['xml']) {
- $this->addParser(
- ['application/xml', 'text/xml'],
- [$this, 'decodeXml']
- );
- }
- if ($options['methods']) {
- $this->setMethods($options['methods']);
- }
- }
- /**
- * Set the HTTP methods to parse request bodies on.
- *
- * @param array $methods The methods to parse data on.
- * @return $this
- */
- public function setMethods(array $methods)
- {
- $this->methods = $methods;
- return $this;
- }
- /**
- * Add a parser.
- *
- * Map a set of content-type header values to be parsed by the $parser.
- *
- * ### Example
- *
- * An naive CSV request body parser could be built like so:
- *
- * ```
- * $parser->addParser(['text/csv'], function ($body) {
- * return str_getcsv($body);
- * });
- * ```
- *
- * @param array $types An array of content-type header values to match. eg. application/json
- * @param callable $parser The parser function. Must return an array of data to be inserted
- * into the request.
- * @return $this
- */
- public function addParser(array $types, callable $parser)
- {
- foreach ($types as $type) {
- $type = strtolower($type);
- $this->parsers[$type] = $parser;
- }
- return $this;
- }
- /**
- * Apply the middleware.
- *
- * Will modify the request adding a parsed body if the content-type is known.
- *
- * @param \Psr\Http\Message\ServerRequestInterface $request The request.
- * @param \Psr\Http\Message\ResponseInterface $response The response.
- * @param callable $next Callback to invoke the next middleware.
- * @return \Cake\Http\Response A response
- */
- public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
- {
- if (!in_array($request->getMethod(), $this->methods)) {
- return $next($request, $response);
- }
- list($type) = explode(';', $request->getHeaderLine('Content-Type'));
- $type = strtolower($type);
- if (!isset($this->parsers[$type])) {
- return $next($request, $response);
- }
- $parser = $this->parsers[$type];
- $result = $parser($request->getBody()->getContents());
- if (!is_array($result)) {
- throw new BadRequestException();
- }
- $request = $request->withParsedBody($result);
- return $next($request, $response);
- }
- /**
- * Decode JSON into an array.
- *
- * @param string $body The request body to decode
- * @return array
- */
- protected function decodeJson($body)
- {
- return json_decode($body, true);
- }
- /**
- * Decode XML into an array.
- *
- * @param string $body The request body to decode
- * @return array
- */
- protected function decodeXml($body)
- {
- try {
- $xml = Xml::build($body, ['return' => 'domdocument', 'readFile' => false]);
- // We might not get child nodes if there are nested inline entities.
- if ($xml->childNodes->length > 0) {
- return Xml::toArray($xml);
- }
- return [];
- } catch (XmlException $e) {
- return [];
- }
- }
- }
|