zendframework / zend-stratigility

Middleware for PHP built on top of PSR-7 and PSR-15
BSD 3-Clause "New" or "Revised" License
235 stars 57 forks source link

[RFC] HTTP method overriding middleware #171

Closed corentin-larose closed 6 years ago

corentin-larose commented 6 years ago

I propose to create a middleware overriding the HTTP method when _method parameter is provided in the GET or POST payload (ie ?_method=DELETE).

Some options I also consider:

validateMethod has been derived from the request trait.

If interest shown, I will create the file and unit tests in a PR. Should that middleware be provided as a "module"?

<?php

declare(strict_types=1);

namespace Zend\Stratigility\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class HttpMethodOverride implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
    {
        $method = $request->getAttribute('_method', null);

        if (!is_null($method)) {
            $this->validateMethod($method);

            $originalMethod = strtoupper($request->getMethod());

            switch ($originalMethod) {
                case 'GET':
                case 'POST':
                    $request = $request->withMethod(strtoupper($method));
                    break;
                default:
                    throw new InvalidArgumentException(sprintf(
                        'Only GET and POST http methods can be overrode, received %s',
                        $method
                    ));
            }
        }

        return $handler->handle($request);
    }

    /**
     * Validate the HTTP method
     *
     * @param null|string $method
     * @throws InvalidArgumentException on invalid HTTP method.
     */
    private function validateMethod($method)
    {
        if (! is_string($method)) {
            throw new InvalidArgumentException(sprintf(
                'Unsupported HTTP method; must be a string, received %s',
                (is_object($method) ? get_class($method) : gettype($method))
            ));
        }

        if (! preg_match('/^[!#$%&\'*+.^_`\|~0-9a-z-]+$/i', $method)) {
            throw new InvalidArgumentException(sprintf(
                'Unsupported HTTP method "%s" provided',
                $method
            ));
        }
    }
}
weierophinney commented 6 years ago

Honestly, I think this would have general utility for any PSR-7/PSR-15 middleware application, not just Stratigility. Have you considered shipping it as a standalone package?

corentin-larose commented 6 years ago

Hello,

Yes, this is what I wanted to say when writing "Should that middleware be provided as a "module"?". Ok, I will create a standalone package and come back to you guys for comments. What do you think about the configuration options I proposed? I will also come back with a standalone port of my zf-http-cache module.

corentin-larose commented 6 years ago

Then, trying to find a repository to put that codeI just found this: https://github.com/middlewares/method-override. So I close this issue

weierophinney commented 6 years ago

Awesome to see it already exists! Make sure to contribute to it if you find it is missing functionality you need. :smile: