Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTP method override middleware #2329

Merged
merged 4 commits into from Dec 23, 2017
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@

### Added

- [#2329](https://github.com/slimphp/Slim/pull/2329) Added Middleware\MethodOverrideMiddleware
- [#2288](https://github.com/slimphp/Slim/pull/2288) Separate routing from dispatching
- [#2254](https://github.com/slimphp/Slim/pull/2254) Added Middleware\ContentLengthMiddleware
- [#2166](https://github.com/slimphp/Slim/pull/2166) Added Middleware\OutputBufferingMiddleware
Expand Down
47 changes: 47 additions & 0 deletions Slim/Middleware/MethodOverrideMiddleware.php
@@ -0,0 +1,47 @@
<?php
/**
* Slim Framework (https://slimframework.com)
*
* @link https://github.com/slimphp/Slim
* @copyright Copyright (c) 2011-2017 Josh Lockhart
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
*/
namespace Slim\Middleware;

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

/**
* Override HTTP Request method by given body param or custom header
*/
class MethodOverrideMiddleware
{
/**
* Invoke
*
* @param ServerRequestInterface $request PSR7 server request
* @param ResponseInterface $response PSR7 response
* @param callable $next Middleware callable
* @return ResponseInterface PSR7 response
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$methodHeader = $request->getHeaderLine('X-Http-Method-Override');

if ($methodHeader) {
$request = $request->withMethod($methodHeader);
} elseif (strtoupper($request->getMethod()) == 'POST') {
$body = $request->getParsedBody();

if (!empty($body['_METHOD'])) {
$request = $request->withMethod($body['_METHOD']);
}

if ($request->getBody()->eof()) {
$request->getBody()->rewind();
}
}

return $next($request, $response);
}
}
2 changes: 2 additions & 0 deletions UPGRADING.md
@@ -1,5 +1,6 @@
# How to upgrade

* [2329] - If you were overriding the HTTP method using either the custom header or the body param, you need to add the `Middleware\MethodOverrideMiddleware` middleware to be able to override the method like before.
* [2288] - If you were using `determineRouteBeforeAppMiddleware`, you need to add the `Middleware\RoutingMiddleware` middleware to your application just before your call `run()` to maintain the previous behaviour.
* [2254] - You need to add the `Middleware\ContentLengthMiddleware` middleware if you want Slim to add the Content-Length header this automatically.
* [2290] - Slim no longer has a Container so you need to supply your own. As a
Expand All @@ -13,6 +14,7 @@
* [2098] - You need to add the App's router to the container for a straight upgrade. If you've created your own router factory in the container though, then you need to set it into the $app.
* [2102] - You must inject custom route invocation strategy with `$app->getRouter()->setDefaultInvocationStrategy($myStrategy)`

[2329]: https://github.com/slimphp/Slim/pull/2329
[2290]: https://github.com/slimphp/Slim/pull/2290
[2288]: https://github.com/slimphp/Slim/pull/2288
[2254]: https://github.com/slimphp/Slim/pull/2254
Expand Down
111 changes: 111 additions & 0 deletions tests/Middleware/MethodOverrideMiddlewareTest.php
@@ -0,0 +1,111 @@
<?php
/**
* Slim Framework (https://slimframework.com)
*
* @link https://github.com/slimphp/Slim
* @copyright Copyright (c) 2011-2017 Josh Lockhart
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
*/
namespace Slim\Tests\Middleware;

use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Http\Headers;
use Slim\Http\Request;
use Slim\Http\RequestBody;
use Slim\Http\Response;
use Slim\Http\Uri;
use Slim\Middleware\MethodOverrideMiddleware;

/**
* @covers \Slim\Middleware\MethodOverrideMiddleware
*/
class MethodOverrideMiddlewareTest extends TestCase
{
public function testHeader()
{
$mw = new MethodOverrideMiddleware();

$uri = new Uri('http', 'example.com');
$headers = new Headers([
'HTTP_X_HTTP_METHOD_OVERRIDE' => 'PUT',
]);
$request = new Request('GET', $uri, $headers, [], [], new RequestBody());
$response = new Response();

$next = function (ServerRequestInterface $req, ResponseInterface $res) {
$this->assertEquals('PUT', $req->getMethod());

return $res;
};
\Closure::bind($next, $this);

$mw($request, $response, $next);
}

public function testBodyParam()
{
$mw = new MethodOverrideMiddleware();

$uri = new Uri('http', 'example.com');
$body = new RequestBody();
$body->write('_METHOD=PUT');
$headers = new Headers([
'Content-Type' => 'application/x-www-form-urlencoded',
]);
$request = new Request('POST', $uri, $headers, [], [], $body);
$response = new Response();

$next = function (ServerRequestInterface $req, ResponseInterface $res) {
$this->assertEquals('PUT', $req->getMethod());

return $res;
};
\Closure::bind($next, $this);

$mw($request, $response, $next);
}

public function testHeaderPreferred()
{
$mw = new MethodOverrideMiddleware();

$uri = new Uri('http', 'example.com');
$body = new RequestBody();
$body->write('_METHOD=PUT');
$headers = new Headers([
'Content-Type' => 'application/x-www-form-urlencoded',
'HTTP_X_HTTP_METHOD_OVERRIDE' => 'DELETE',
]);
$request = new Request('POST', $uri, $headers, [], [], $body);
$response = new Response();

$next = function (ServerRequestInterface $req, ResponseInterface $res) {
$this->assertEquals('DELETE', $req->getMethod());

return $res;
};
\Closure::bind($next, $this);

$mw($request, $response, $next);
}

public function testNoOverride()
{
$mw = new MethodOverrideMiddleware();

$uri = new Uri('http', 'example.com');
$request = new Request('POST', $uri, new Headers(), [], [], new RequestBody());
$response = new Response();

$next = function (ServerRequestInterface $req, ResponseInterface $res) {
$this->assertEquals('POST', $req->getMethod());

return $res;
};
\Closure::bind($next, $this);

$mw($request, $response, $next);
}
}