Skip to content
This repository has been archived by the owner on Jan 21, 2020. It is now read-only.

Added strategies to the BodyParamsMiddleware #5

Merged
merged 5 commits into from
Dec 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,142 @@ Where:

- `$path`, when provided, can be a string path to use to generate a URI.

### BodyParams middleware

One aspect of PSR-7 is that it allows you to parse the raw request body, and
then create a new instance with the results of parsing that later processes can
fetch via `getParsedBody()`. It does not provide any actual facilities for
parsing, which means you must write middleware to do so.

This package provides such facilities via `Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware`.
By default, this middleware will detect the following content types:

- `application/x-www-form-urlencoded` (standard web-based forms, without file
uploads)
- `application/json`, `application/*+json` (JSON payloads)

You can register it manually:

```php
use Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware;

$app->pipe(BodyParamsMiddleware::class);
```

or, if using Expressive, as pre_routing pipeline middleware:

```php
// config/autoload/middleware-pipeline.global.php
use Zend\Expressive\Helper;

return [
'dependencies' => [
'invokables' => [
Helper\BodyParams\BodyParamsMiddleware::class => Helper\BodyParams\BodyParamsMiddleware::class,
/* ... */
],
'factories' => [
/* ... */
],
],
'middleware_pipeline' => [
'pre_routing' => [
[ 'middleware' => Helper\BodyParams\BodyParamsMiddleware::class ],
/* ... */
],
'post_routing' => [
/* ... */
],
],
];
```

#### Strategies

If you want to intercept and parse other payload types, you can add *strategies*
to the middleware. Strategies implement `Zend\Expressive\Helper\BodyParams\StrategyInterface`:

```php
namespace Zend\Expressive\Helper\BodyParams;

use Psr\Http\Message\ServerRequestInterface;

interface StrategyInterface
{
/**
* Match the content type to the strategy criteria.
*
* @param string $contentType
* @return bool Whether or not the strategy matches.
*/
public function match($contentType);

/**
* Parse the body content and return a new response.
*
* @param ServerRequestInterface $request
* @return ServerRequestInterface
*/
public function parse(ServerRequestInterface $request);
}
```

You then register them with the middleware using the `addStrategy()` method:

```php
$bodyParams->addStrategy(new MyCustomBodyParamsStrategy());
```

To automate the registration, we recommend writing a factory for the
`BodyParamsMiddleware`, and replacing the `invokables` registration with a
registration in the `factories` section of the `middleware-pipeline.config.php`
file:

```php
use Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware;

class MyCustomBodyParamsStrategyFactory
{
public function __invoke($container)
{
$bodyParams = new BodyParamsMiddleware();
$bodyParams->addStrategy(new MyCustomBodyParamsStrategy());
return $bodyParams;
}
}

// In config/autoload/middleware-pipeline.config.php:
use Zend\Expressive\Helper;

return [
'dependencies' => [
'invokables' => [
// Remove this line:
Helper\BodyParams\BodyParamsMiddleware::class => Helper\BodyParams\BodyParamsMiddleware::class,
/* ... */
],
'factories' => [
// Add this line:
Helper\BodyParams\BodyParamsMiddleware::class => MyCustomBodyParamsStrategy::class,
/* ... */
],
],
];
```

#### Removing the default strategies

If you do not want to use the default strategies (form data and JSON), you can
clear them from the middleware using `clearStrategies()`:

```php
$bodyParamsMiddleware->clearStrategies();
```

Note: if you do this, **all** strategies will be removed! As such, we recommend
doing this only immediately before registering any custom strategies you might
be using.

## Documentation

See the [zend-expressive](https://github.com/zendframework/zend-expressive/blob/master/doc/book)
Expand Down
94 changes: 94 additions & 0 deletions src/BodyParams/BodyParamsMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php
/**
* @see http://github.com/zendframework/zend-expressive for the canonical source repository
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-expressive/blob/master/LICENSE.md New BSD License
*/

namespace Zend\Expressive\Helper\BodyParams;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class BodyParamsMiddleware
{
/**
* @var StrategyInterface[]
*/
private $strategies = [];

/**
* List of request methods that do not have any defined body semantics, and thus
* will not have the body parsed.
*
* @see https://tools.ietf.org/html/rfc7231
*
* @var array
*/
private $nonBodyRequests = [
'GET',
'HEAD',
'OPTIONS',
];

/**
* Constructor
*
* Registers the form and json strategies.
*/
public function __construct()
{
$this->addStrategy(new FormUrlEncodedStrategy());
$this->addStrategy(new JsonStrategy());
}

/**
* Add a body parsing strategy to the middleware.
*
* @param StrategyInterface $strategy
*/
public function addStrategy(StrategyInterface $strategy)
{
$this->strategies[] = $strategy;
}

/**
* Clear all strategies from the middleware.
*/
public function clearStrategies()
{
$this->strategies = [];
}

/**
* Adds JSON decoded request body to the request, where appropriate.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
if (in_array($request->getMethod(), $this->nonBodyRequests)) {
return $next($request, $response);
}

$header = $request->getHeaderLine('Content-Type');
foreach ($this->strategies as $strategy) {
if (! $strategy->match($header)) {
continue;
}

// Matched! Parse and pass on to the next
return $next(
$strategy->parse($request),
$response
);
}

// No match; continue
return $next($request, $response);
}
}
29 changes: 29 additions & 0 deletions src/BodyParams/FormUrlEncodedStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php
/**
* @see http://github.com/zendframework/zend-expressive-helpers for the canonical source repository
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-helpers/blob/master/LICENSE.md New BSD License
*/

namespace Zend\Expressive\Helper\BodyParams;

use Psr\Http\Message\ServerRequestInterface;

class FormUrlEncodedStrategy implements StrategyInterface
{
/**
* {@inheritDoc}
*/
public function match($contentType)
{
return (bool) preg_match('#^application/x-www-form-urlencoded($|[ ;])#', $contentType);
}

/**
* {@inheritDoc}
*/
public function parse(ServerRequestInterface $request)
{
return $request;
}
}
34 changes: 34 additions & 0 deletions src/BodyParams/JsonStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/**
* @see http://github.com/zendframework/zend-expressive-helpers for the canonical source repository
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-helpers/blob/master/LICENSE.md New BSD License
*/

namespace Zend\Expressive\Helper\BodyParams;

use Psr\Http\Message\ServerRequestInterface;

class JsonStrategy implements StrategyInterface
{
/**
* {@inheritDoc}
*/
public function match($contentType)
{
$parts = explode(';', $contentType);
$mime = array_shift($parts);
return (bool) preg_match('#[/+]json$#', trim($mime));
}

/**
* {@inheritDoc}
*/
public function parse(ServerRequestInterface $request)
{
$rawBody = $request->getBody()->getContents();
return $request
->withAttribute('rawBody', $rawBody)
->withParsedBody(json_decode($rawBody, true));
}
}
32 changes: 32 additions & 0 deletions src/BodyParams/StrategyInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* @see http://github.com/zendframework/zend-expressive-helpers for the canonical source repository
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-helpers/blob/master/LICENSE.md New BSD License
*/

namespace Zend\Expressive\Helper\BodyParams;

use Psr\Http\Message\ServerRequestInterface;

/**
* Interface defining a body parameter strategy.
*/
interface StrategyInterface
{
/**
* Match the content type to the strategy criteria.
*
* @param string $contentType
* @return bool Whether or not the strategy matches.
*/
public function match($contentType);

/**
* Parse the body content and return a new response.
*
* @param ServerRequestInterface $request
* @return ServerRequestInterface
*/
public function parse(ServerRequestInterface $request);
}
Loading