From c954cadfe0d8a48619e6a78378dc73ce663b0194 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Tue, 22 Oct 2024 19:44:06 +0700 Subject: [PATCH 01/11] Add options to control method failure handling --- src/Middleware/Router.php | 44 ++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/Middleware/Router.php b/src/Middleware/Router.php index 631823ff..31aec2cd 100644 --- a/src/Middleware/Router.php +++ b/src/Middleware/Router.php @@ -19,6 +19,8 @@ final class Router implements MiddlewareInterface { + private bool $handleMethodFailure = true; + private MiddlewareDispatcher $dispatcher; public function __construct( @@ -26,7 +28,9 @@ public function __construct( private ResponseFactoryInterface $responseFactory, MiddlewareFactory $middlewareFactory, private CurrentRoute $currentRoute, - ?EventDispatcherInterface $eventDispatcher = null + ?EventDispatcherInterface $eventDispatcher = null, + private ?RequestHandlerInterface $optionsHandler = null, + private ?RequestHandlerInterface $notAllowedHandler = null ) { $this->dispatcher = new MiddlewareDispatcher($middlewareFactory, $eventDispatcher); } @@ -37,15 +41,10 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $this->currentRoute->setUri($request->getUri()); - if ($result->isMethodFailure()) { - if ($request->getMethod() === Method::OPTIONS) { - return $this->responseFactory - ->createResponse(Status::NO_CONTENT) - ->withHeader('Allow', implode(', ', $result->methods())); - } - return $this->responseFactory - ->createResponse(Status::METHOD_NOT_ALLOWED) - ->withHeader('Allow', implode(', ', $result->methods())); + if ($this->handleMethodFailure && $result->isMethodFailure()) { + return $request->getMethod() === Method::OPTIONS + ? $this->getOptionsResponse($request, $result) + : $this->getMethodNotAllowedResponse($request, $result); } if (!$result->isSuccess()) { @@ -58,4 +57,29 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface ->withDispatcher($this->dispatcher) ->process($request, $handler); } + + public function withHandleMethodFailure(bool $handleMethodFailure): self + { + $new = clone $this; + $new->handleMethodFailure = $handleMethodFailure; + return $new; + } + + private function getOptionsResponse(ServerRequestInterface $request, MatchingResult $result): ResponseInterface + { + return $this->optionsHandler !== null + ? $this->optionsHandler->handle($request, $result) + : $this->responseFactory + ->createResponse(Status::NO_CONTENT) + ->withHeader(Header::ALLOW, $result->methods()); + } + + private function getMethodNotAllowedResponse(ServerRequestInterface $request, MatchingResult $result): ResponseInterface + { + return $this->notAllowedHandler !== null + ? $this->notAllowedHandler->handle($request, $result) + : $this->responseFactory + ->createResponse(Status::METHOD_NOT_ALLOWED) + ->withHeader(Header::ALLOW, implode(', ', $result->methods())); + } } From c96eb009c9eeb49b7060ce479d7c9cc6ef5e33cc Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Fri, 25 Oct 2024 18:58:24 +0700 Subject: [PATCH 02/11] add Router::ignoreMethodFailureHandle option add factories for the method failure responses --- CHANGELOG.md | 3 +- README.md | 134 ++++++++++++++++++++---- src/MethodsResponseFactoryInterface.php | 22 ++++ src/Middleware/Router.php | 51 +++++---- tests/Middleware/RouterTest.php | 61 ++++++++++- 5 files changed, 227 insertions(+), 44 deletions(-) create mode 100644 src/MethodsResponseFactoryInterface.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a37995d7..451f99c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ## 3.1.1 under development -- no changes in this release. +- New #249 Add to the 'Router' middleware option to ignore method failure handle (@olegbaturin) +- New #249 Add to the 'Router' middleware custom response factories for the method failure responses (@olegbaturin) ## 3.1.0 February 20, 2024 diff --git a/README.md b/README.md index 744a901b..8b2eaa31 100644 --- a/README.md +++ b/README.md @@ -106,22 +106,6 @@ $response = $result->process($request, $notFoundHandler); > to specific adapter documentation. All examples in this document are for > [FastRoute adapter](https://github.com/yiisoft/router-fastroute). -### Middleware usage - -In order to simplify usage in PSR-middleware based application, there is a ready to use middleware provided: - -```php -$router = $container->get(Yiisoft\Router\UrlMatcherInterface::class); -$responseFactory = $container->get(\Psr\Http\Message\ResponseFactoryInterface::class); - -$routerMiddleware = new Yiisoft\Router\Middleware\Router($router, $responseFactory, $container); - -// Add middleware to your middleware handler of choice. -``` - -In case of a route match router middleware executes handler middleware attached to the route. If there is no match, next -application middleware processes the request. - ### Routes Route could match for one or more HTTP methods: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`. There are @@ -233,17 +217,127 @@ and `disableMiddleware()`. These middleware are executed prior to matched route' If host is specified, all routes in the group would match only if the host match. -### Automatic OPTIONS response and CORS +### Middleware usage + +In order to simplify usage in PSR-middleware based application, there is a ready to use `Yiisoft\Router\Middleware\Router` middleware provided: + +```php +$router = $container->get(Yiisoft\Router\UrlMatcherInterface::class); +$responseFactory = $container->get(\Psr\Http\Message\ResponseFactoryInterface::class); + +$routerMiddleware = new Yiisoft\Router\Middleware\Router($router, $responseFactory, $container); + +// Add middleware to your middleware handler of choice. +``` + +In case of a route match router middleware executes handler middleware attached to the route. If there is no match, next +application middleware processes the request. + +### Automatic responses + +`Yiisoft\Router\Middleware\Router` middleware responds automatically to: +- `OPTIONS` requests; +- requests with methods that are not supported by the target resource. + +You can disable this behavior by calling the `Yiisoft\Router\Middleware\Router::ignoreMethodFailureHandle()` method + +```php +use Yiisoft\Router\Middleware\Router; + +$routerMiddleware = new Router($router, $responseFactory, $middlewareFactory, $currentRoute); + +// Returns a new instance with the turned off method failure error handle. +$routerMiddleware = $routerMiddleware->ignoreMethodFailureHandle(); +``` + +or define the `Yiisoft\Router\Middleware\Router` configuration in the DI container + +`config/common/di/router.php`: + +```php +use Yiisoft\Router\Middleware\Router; + +return [ + Router::class => [ + 'ignoreMethodFailureHandle()' => [], + ], +]; +``` + +#### OPTIONS requests -By default, router responds automatically to OPTIONS requests based on the routes defined: +By default, `Yiisoft\Router\Middleware\Router` middleware responds to `OPTIONS` requests based on the routes defined: ``` HTTP/1.1 204 No Content Allow: GET, HEAD ``` -Generally that is fine unless you need [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). In this -case, you can add a middleware for handling it such as [tuupola/cors-middleware](https://github.com/tuupola/cors-middleware): +You can setup a custom response factory by calling the `Yiisoft\Router\Middleware\Router::withOptionsResponseFactory()` method + +```php +use Yiisoft\Router\Middleware\Router; + +$routerMiddleware = new Router($router, $responseFactory, $middlewareFactory, $currentRoute); +$optionsResponseFactory = new OptionsResponseFactory(); + +// Returns a new instance with the response factory. +$routerMiddleware = $routerMiddleware->withOptionsResponseFactory($optionsResponseFactory); +``` + +or define the `Yiisoft\Router\Middleware\Router` configuration in the DI container + +`config/common/di/router.php`: + +```php +use Yiisoft\Router\Middleware\Router; + +return [ + Router::class => [ + 'withOptionsResponseFactory()' => [Reference::to(OptionsResponseFactory::class)], + ], +]; +``` + + +#### Method not allowed + +By default, `Yiisoft\Router\Middleware\Router` middleware responds to requests with methods that are not supported by the target resource based on the routes defined: + +``` +HTTP/1.1 405 Method Not Allowed +Allow: GET, HEAD +``` + +You can setup a custom response factory by calling `Yiisoft\Router\Middleware\Router::withNotAllowedResponseFactory()` method + +```php +use Yiisoft\Router\Middleware\Router; + +$routerMiddleware = new Router($router, $responseFactory, $middlewareFactory, $currentRoute); +$notAllowedResponseFactory = new NotAllowedResponseFactory(); + +// Returns a new instance with the response factory. +$routerMiddleware = $routerMiddleware->withNotAllowedResponseFactory($notAllowedResponseFactory); +``` + +or define the `Yiisoft\Router\Middleware\Router` configuration in the DI container + +`config/common/di/router.php`: + +```php +use Yiisoft\Router\Middleware\Router; + +return [ + Router::class => [ + 'withNotAllowedResponseFactory()' => [Reference::to(NotAllowedResponseFactory::class)], + ], +]; +``` + +### CORS protocol + +If you need [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) you can add a middleware for handling it such as [tuupola/cors-middleware](https://github.com/tuupola/cors-middleware): ```php use Yiisoft\Router\Group; diff --git a/src/MethodsResponseFactoryInterface.php b/src/MethodsResponseFactoryInterface.php new file mode 100644 index 00000000..5ab5763e --- /dev/null +++ b/src/MethodsResponseFactoryInterface.php @@ -0,0 +1,22 @@ +dispatcher = new MiddlewareDispatcher($middlewareFactory, $eventDispatcher); } @@ -41,10 +42,10 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $this->currentRoute->setUri($request->getUri()); - if ($this->handleMethodFailure && $result->isMethodFailure()) { + if (!$this->ignoreMethodFailureHandle && $result->isMethodFailure()) { return $request->getMethod() === Method::OPTIONS - ? $this->getOptionsResponse($request, $result) - : $this->getMethodNotAllowedResponse($request, $result); + ? $this->getOptionsResponse($request, $result->methods()) + : $this->getMethodNotAllowedResponse($request, $result->methods()); } if (!$result->isSuccess()) { @@ -58,28 +59,42 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface ->process($request, $handler); } - public function withHandleMethodFailure(bool $handleMethodFailure): self + public function ignoreMethodFailureHandle(): self + { + $new = clone $this; + $new->ignoreMethodFailureHandle = true; + return $new; + } + + public function withOptionsResponseFactory(MethodsResponseFactoryInterface $optionsResponseFactory): self + { + $new = clone $this; + $new->optionsResponseFactory = $optionsResponseFactory; + return $new; + } + + public function withNotAllowedResponseFactory(MethodsResponseFactoryInterface $notAllowedResponseFactory): self { $new = clone $this; - $new->handleMethodFailure = $handleMethodFailure; + $new->notAllowedResponseFactory = $notAllowedResponseFactory; return $new; } - private function getOptionsResponse(ServerRequestInterface $request, MatchingResult $result): ResponseInterface + private function getOptionsResponse(ServerRequestInterface $request, array $methods): ResponseInterface { - return $this->optionsHandler !== null - ? $this->optionsHandler->handle($request, $result) + return $this->optionsResponseFactory !== null + ? $this->optionsResponseFactory->create($methods, $request) : $this->responseFactory ->createResponse(Status::NO_CONTENT) - ->withHeader(Header::ALLOW, $result->methods()); + ->withHeader(Header::ALLOW, implode(', ', $methods)); } - private function getMethodNotAllowedResponse(ServerRequestInterface $request, MatchingResult $result): ResponseInterface + private function getMethodNotAllowedResponse(ServerRequestInterface $request, array $methods): ResponseInterface { - return $this->notAllowedHandler !== null - ? $this->notAllowedHandler->handle($request, $result) + return $this->notAllowedResponseFactory !== null + ? $this->notAllowedResponseFactory->create($methods, $request) : $this->responseFactory ->createResponse(Status::METHOD_NOT_ALLOWED) - ->withHeader(Header::ALLOW, implode(', ', $result->methods())); + ->withHeader(Header::ALLOW, implode(', ', $methods)); } } diff --git a/tests/Middleware/RouterTest.php b/tests/Middleware/RouterTest.php index 13c7695e..9c8e3a67 100644 --- a/tests/Middleware/RouterTest.php +++ b/tests/Middleware/RouterTest.php @@ -17,6 +17,7 @@ use Yiisoft\Router\CurrentRoute; use Yiisoft\Router\Group; use Yiisoft\Router\MatchingResult; +use Yiisoft\Router\MethodsResponseFactoryInterface; use Yiisoft\Router\Middleware\Router; use Yiisoft\Router\Route; use Yiisoft\Router\RouteCollection; @@ -50,6 +51,25 @@ public function testMethodMismatchRespondWith405(): void $this->assertSame('GET, HEAD', $response->getHeaderLine('Allow')); } + public function testMethodMismatchFactoryRespondWith400(): void + { + $notAllowedResponseFactory = new class () implements MethodsResponseFactoryInterface { + public function create(array $methods, ServerRequestInterface $request): ResponseInterface + { + return (new Response(400)) + ->withHeader('Test', 'test from options handler'); + } + }; + + $request = new ServerRequest('POST', '/'); + $response = $this + ->createRouterMiddleware() + ->withNotAllowedResponseFactory($notAllowedResponseFactory) + ->process($request, $this->createRequestHandler()); + $this->assertSame(400, $response->getStatusCode()); + $this->assertSame('test from options handler', $response->getHeaderLine('Test')); + } + public function testAutoResponseOptions(): void { $request = new ServerRequest('OPTIONS', '/'); @@ -58,6 +78,37 @@ public function testAutoResponseOptions(): void $this->assertSame('GET, HEAD', $response->getHeaderLine('Allow')); } + public function testAutoResponseOptionsFactory(): void + { + $optionsResponseFactory = new class () implements MethodsResponseFactoryInterface { + public function create(array $methods, ServerRequestInterface $request): ResponseInterface + { + return (new Response(200)) + ->withHeader('Allow', implode(', ', $methods)) + ->withHeader('Test', 'test from options handler'); + } + }; + + $request = new ServerRequest('OPTIONS', '/'); + $response = $this + ->createRouterMiddleware() + ->withOptionsResponseFactory($optionsResponseFactory) + ->process($request, $this->createRequestHandler()); + $this->assertSame(200, $response->getStatusCode()); + $this->assertSame('GET, HEAD', $response->getHeaderLine('Allow')); + $this->assertSame('test from options handler', $response->getHeaderLine('Test')); + } + + public function testIgnoreMethodFailureHandleRespondWith404(): void + { + $request = new ServerRequest('POST', '/'); + $response = $this + ->createRouterMiddleware() + ->ignoreMethodFailureHandle() + ->process($request, $this->createRequestHandler()); + $this->assertSame(404, $response->getStatusCode()); + } + public function testAutoResponseOptionsWithOrigin(): void { $request = new ServerRequest('OPTIONS', 'http://test.local/', ['Origin' => 'http://test.com']); @@ -262,9 +313,9 @@ public function createResponse(int $code = 200, string $reasonPhrase = ''): Resp } private function createRouterMiddleware( - ?RouteCollectionInterface $routeCollection = null, - ?CurrentRoute $currentRoute = null, - array $containerDefinitions = [], + RouteCollectionInterface $routeCollection = null, + CurrentRoute $currentRoute = null, + array $containerDefinitions = [] ): Router { $container = new SimpleContainer( array_merge( @@ -283,8 +334,8 @@ private function createRouterMiddleware( private function processWithRouter( ServerRequestInterface $request, - ?RouteCollectionInterface $routes = null, - ?CurrentRoute $currentRoute = null, + RouteCollectionInterface $routes = null, + CurrentRoute $currentRoute = null, array $containerDefinitions = [], ): ResponseInterface { return $this From 5603689208f1de60749be310a35b252c2d1ae8e4 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Fri, 25 Oct 2024 19:06:34 +0700 Subject: [PATCH 03/11] fix psalm --- src/Middleware/Router.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Middleware/Router.php b/src/Middleware/Router.php index 0f0e3ea2..76cadac3 100644 --- a/src/Middleware/Router.php +++ b/src/Middleware/Router.php @@ -80,6 +80,9 @@ public function withNotAllowedResponseFactory(MethodsResponseFactoryInterface $n return $new; } + /** + * @param string[] $methods + */ private function getOptionsResponse(ServerRequestInterface $request, array $methods): ResponseInterface { return $this->optionsResponseFactory !== null @@ -89,6 +92,9 @@ private function getOptionsResponse(ServerRequestInterface $request, array $meth ->withHeader(Header::ALLOW, implode(', ', $methods)); } + /** + * @param string[] $methods + */ private function getMethodNotAllowedResponse(ServerRequestInterface $request, array $methods): ResponseInterface { return $this->notAllowedResponseFactory !== null From be42761a32f15e00cefb865d628f9f09abf100e8 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Fri, 25 Oct 2024 19:13:13 +0700 Subject: [PATCH 04/11] fix styles --- src/MethodsResponseFactoryInterface.php | 1 - tests/Middleware/RouterTest.php | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/MethodsResponseFactoryInterface.php b/src/MethodsResponseFactoryInterface.php index 5ab5763e..b42a5878 100644 --- a/src/MethodsResponseFactoryInterface.php +++ b/src/MethodsResponseFactoryInterface.php @@ -16,7 +16,6 @@ interface MethodsResponseFactoryInterface * Handles allowed methods and produces a response. * * @param array $methods a list of the HTTP methods supported by the request's resource - * */ public function create(array $methods, ServerRequestInterface $request): ResponseInterface; } diff --git a/tests/Middleware/RouterTest.php b/tests/Middleware/RouterTest.php index 9c8e3a67..1e68f508 100644 --- a/tests/Middleware/RouterTest.php +++ b/tests/Middleware/RouterTest.php @@ -53,7 +53,7 @@ public function testMethodMismatchRespondWith405(): void public function testMethodMismatchFactoryRespondWith400(): void { - $notAllowedResponseFactory = new class () implements MethodsResponseFactoryInterface { + $notAllowedResponseFactory = new class () implements MethodsResponseFactoryInterface { public function create(array $methods, ServerRequestInterface $request): ResponseInterface { return (new Response(400)) @@ -80,7 +80,7 @@ public function testAutoResponseOptions(): void public function testAutoResponseOptionsFactory(): void { - $optionsResponseFactory = new class () implements MethodsResponseFactoryInterface { + $optionsResponseFactory = new class () implements MethodsResponseFactoryInterface { public function create(array $methods, ServerRequestInterface $request): ResponseInterface { return (new Response(200)) From 7d730d0eca6ca56aa00de488027e1f92d85c20c7 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Fri, 25 Oct 2024 19:51:36 +0700 Subject: [PATCH 05/11] update tests --- tests/Middleware/RouterTest.php | 55 +++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/tests/Middleware/RouterTest.php b/tests/Middleware/RouterTest.php index 1e68f508..6c6015e7 100644 --- a/tests/Middleware/RouterTest.php +++ b/tests/Middleware/RouterTest.php @@ -53,18 +53,10 @@ public function testMethodMismatchRespondWith405(): void public function testMethodMismatchFactoryRespondWith400(): void { - $notAllowedResponseFactory = new class () implements MethodsResponseFactoryInterface { - public function create(array $methods, ServerRequestInterface $request): ResponseInterface - { - return (new Response(400)) - ->withHeader('Test', 'test from options handler'); - } - }; - $request = new ServerRequest('POST', '/'); $response = $this ->createRouterMiddleware() - ->withNotAllowedResponseFactory($notAllowedResponseFactory) + ->withNotAllowedResponseFactory($this->createNotAllowedResponseFactory()) ->process($request, $this->createRequestHandler()); $this->assertSame(400, $response->getStatusCode()); $this->assertSame('test from options handler', $response->getHeaderLine('Test')); @@ -80,19 +72,10 @@ public function testAutoResponseOptions(): void public function testAutoResponseOptionsFactory(): void { - $optionsResponseFactory = new class () implements MethodsResponseFactoryInterface { - public function create(array $methods, ServerRequestInterface $request): ResponseInterface - { - return (new Response(200)) - ->withHeader('Allow', implode(', ', $methods)) - ->withHeader('Test', 'test from options handler'); - } - }; - $request = new ServerRequest('OPTIONS', '/'); $response = $this ->createRouterMiddleware() - ->withOptionsResponseFactory($optionsResponseFactory) + ->withOptionsResponseFactory($this->createOptionsResponseFactory()) ->process($request, $this->createRequestHandler()); $this->assertSame(200, $response->getStatusCode()); $this->assertSame('GET, HEAD', $response->getHeaderLine('Allow')); @@ -257,6 +240,17 @@ public function testRouteMiddleware(int $expectedCode, mixed $middleware): void $this->assertSame($expectedCode, $response->getStatusCode()); } + public function testImmutability(): void + { + $original = $this->createRouterMiddleware(); + + $this->assertNotSame($original, $original + ->ignoreMethodFailureHandle() + ->withNotAllowedResponseFactory($this->createNotAllowedResponseFactory()) + ->withOptionsResponseFactory($this->createOptionsResponseFactory()) + ); + } + private function getMatcher(?RouteCollectionInterface $routeCollection = null): UrlMatcherInterface { $middleware = $this->createRouteMiddleware(); @@ -357,4 +351,27 @@ private function createRouteMiddleware(): callable { return static fn () => new Response(201); } + + private function createNotAllowedResponseFactory(): MethodsResponseFactoryInterface + { + return new class () implements MethodsResponseFactoryInterface { + public function create(array $methods, ServerRequestInterface $request): ResponseInterface + { + return (new Response(400)) + ->withHeader('Test', 'test from options handler'); + } + }; + } + + private function createOptionsResponseFactory(): MethodsResponseFactoryInterface + { + return new class () implements MethodsResponseFactoryInterface { + public function create(array $methods, ServerRequestInterface $request): ResponseInterface + { + return (new Response(200)) + ->withHeader('Allow', implode(', ', $methods)) + ->withHeader('Test', 'test from options handler'); + } + }; + } } From 303a683b489a1d6e88aee88caab1771741dfa390 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Fri, 25 Oct 2024 19:53:39 +0700 Subject: [PATCH 06/11] fix styles --- tests/Middleware/RouterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Middleware/RouterTest.php b/tests/Middleware/RouterTest.php index 6c6015e7..0466d1ca 100644 --- a/tests/Middleware/RouterTest.php +++ b/tests/Middleware/RouterTest.php @@ -351,7 +351,7 @@ private function createRouteMiddleware(): callable { return static fn () => new Response(201); } - + private function createNotAllowedResponseFactory(): MethodsResponseFactoryInterface { return new class () implements MethodsResponseFactoryInterface { From 0479e23974ac5803a43fe3043410a1800a58906c Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Fri, 25 Oct 2024 19:57:59 +0700 Subject: [PATCH 07/11] fix styles --- tests/Middleware/RouterTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Middleware/RouterTest.php b/tests/Middleware/RouterTest.php index 0466d1ca..becc6877 100644 --- a/tests/Middleware/RouterTest.php +++ b/tests/Middleware/RouterTest.php @@ -244,7 +244,9 @@ public function testImmutability(): void { $original = $this->createRouterMiddleware(); - $this->assertNotSame($original, $original + $this->assertNotSame( + $original, + $original ->ignoreMethodFailureHandle() ->withNotAllowedResponseFactory($this->createNotAllowedResponseFactory()) ->withOptionsResponseFactory($this->createOptionsResponseFactory()) From bd6862445c287209540f60572c64cd48797072d5 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Fri, 25 Oct 2024 20:22:27 +0700 Subject: [PATCH 08/11] update immutability test --- tests/Middleware/RouterTest.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/Middleware/RouterTest.php b/tests/Middleware/RouterTest.php index becc6877..c3d661a1 100644 --- a/tests/Middleware/RouterTest.php +++ b/tests/Middleware/RouterTest.php @@ -244,13 +244,9 @@ public function testImmutability(): void { $original = $this->createRouterMiddleware(); - $this->assertNotSame( - $original, - $original - ->ignoreMethodFailureHandle() - ->withNotAllowedResponseFactory($this->createNotAllowedResponseFactory()) - ->withOptionsResponseFactory($this->createOptionsResponseFactory()) - ); + $this->assertNotSame($original, $original->ignoreMethodFailureHandle()); + $this->assertNotSame($original, $original->withNotAllowedResponseFactory($this->createNotAllowedResponseFactory())); + $this->assertNotSame($original, $original->withOptionsResponseFactory($this->createOptionsResponseFactory())); } private function getMatcher(?RouteCollectionInterface $routeCollection = null): UrlMatcherInterface From 8c7134c1a12ee4a4886bf42d5b20aae5c3bed119 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Mon, 28 Oct 2024 14:09:26 +0700 Subject: [PATCH 09/11] rename $ignoreMethodFailureHandle to $ignoreMethodFailureHandler --- src/Middleware/Router.php | 8 ++++---- tests/Middleware/RouterTest.php | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Middleware/Router.php b/src/Middleware/Router.php index 76cadac3..ed290415 100644 --- a/src/Middleware/Router.php +++ b/src/Middleware/Router.php @@ -22,7 +22,7 @@ final class Router implements MiddlewareInterface { private MiddlewareDispatcher $dispatcher; - private bool $ignoreMethodFailureHandle = false; + private bool $ignoreMethodFailureHandler = false; private ?MethodsResponseFactoryInterface $optionsResponseFactory = null; private ?MethodsResponseFactoryInterface $notAllowedResponseFactory = null; @@ -42,7 +42,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $this->currentRoute->setUri($request->getUri()); - if (!$this->ignoreMethodFailureHandle && $result->isMethodFailure()) { + if (!$this->ignoreMethodFailureHandler && $result->isMethodFailure()) { return $request->getMethod() === Method::OPTIONS ? $this->getOptionsResponse($request, $result->methods()) : $this->getMethodNotAllowedResponse($request, $result->methods()); @@ -59,10 +59,10 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface ->process($request, $handler); } - public function ignoreMethodFailureHandle(): self + public function ignoreMethodFailureHandler(): self { $new = clone $this; - $new->ignoreMethodFailureHandle = true; + $new->ignoreMethodFailureHandler = true; return $new; } diff --git a/tests/Middleware/RouterTest.php b/tests/Middleware/RouterTest.php index c3d661a1..3f5554e4 100644 --- a/tests/Middleware/RouterTest.php +++ b/tests/Middleware/RouterTest.php @@ -82,12 +82,12 @@ public function testAutoResponseOptionsFactory(): void $this->assertSame('test from options handler', $response->getHeaderLine('Test')); } - public function testIgnoreMethodFailureHandleRespondWith404(): void + public function testIgnoreMethodFailureHandlerRespondWith404(): void { $request = new ServerRequest('POST', '/'); $response = $this ->createRouterMiddleware() - ->ignoreMethodFailureHandle() + ->ignoreMethodFailureHandler() ->process($request, $this->createRequestHandler()); $this->assertSame(404, $response->getStatusCode()); } From 97b76e3e81db51cd7bfb2515c1b3855e87822f54 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Mon, 28 Oct 2024 14:12:14 +0700 Subject: [PATCH 10/11] update readme --- CHANGELOG.md | 4 ++-- README.md | 28 ++++++++++++------------- src/MethodsResponseFactoryInterface.php | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 451f99c9..83b0f98c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,8 @@ ## 3.1.1 under development -- New #249 Add to the 'Router' middleware option to ignore method failure handle (@olegbaturin) -- New #249 Add to the 'Router' middleware custom response factories for the method failure responses (@olegbaturin) +- New #249 Add option to ignore method failure handler to the 'Router' middleware (@olegbaturin) +- New #249 Add custom response factories for the method failure responses to the 'Router' middleware (@olegbaturin) ## 3.1.0 February 20, 2024 diff --git a/README.md b/README.md index 8b2eaa31..f73195df 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,7 @@ If host is specified, all routes in the group would match only if the host match ### Middleware usage -In order to simplify usage in PSR-middleware based application, there is a ready to use `Yiisoft\Router\Middleware\Router` middleware provided: +To simplify usage in PSR-middleware based application, there is a ready to use `Yiisoft\Router\Middleware\Router` middleware provided: ```php $router = $container->get(Yiisoft\Router\UrlMatcherInterface::class); @@ -230,7 +230,7 @@ $routerMiddleware = new Yiisoft\Router\Middleware\Router($router, $responseFacto // Add middleware to your middleware handler of choice. ``` -In case of a route match router middleware executes handler middleware attached to the route. If there is no match, next +When a route matches router middleware executes handler middleware attached to the route. If there is no match, next application middleware processes the request. ### Automatic responses @@ -239,27 +239,27 @@ application middleware processes the request. - `OPTIONS` requests; - requests with methods that are not supported by the target resource. -You can disable this behavior by calling the `Yiisoft\Router\Middleware\Router::ignoreMethodFailureHandle()` method +You can disable this behavior by calling the `Yiisoft\Router\Middleware\Router::ignoreMethodFailureHandler()` method: ```php use Yiisoft\Router\Middleware\Router; $routerMiddleware = new Router($router, $responseFactory, $middlewareFactory, $currentRoute); -// Returns a new instance with the turned off method failure error handle. -$routerMiddleware = $routerMiddleware->ignoreMethodFailureHandle(); +// Returns a new instance with the turned off method failure error handler. +$routerMiddleware = $routerMiddleware->ignoreMethodFailureHandler(); ``` -or define the `Yiisoft\Router\Middleware\Router` configuration in the DI container +or define the `Yiisoft\Router\Middleware\Router` configuration in the DI container: -`config/common/di/router.php`: +`config/common/di/router.php` ```php use Yiisoft\Router\Middleware\Router; return [ Router::class => [ - 'ignoreMethodFailureHandle()' => [], + 'ignoreMethodFailureHandler()' => [], ], ]; ``` @@ -273,7 +273,7 @@ HTTP/1.1 204 No Content Allow: GET, HEAD ``` -You can setup a custom response factory by calling the `Yiisoft\Router\Middleware\Router::withOptionsResponseFactory()` method +You can setup a custom response factory by calling the `Yiisoft\Router\Middleware\Router::withOptionsResponseFactory()` method: ```php use Yiisoft\Router\Middleware\Router; @@ -285,9 +285,9 @@ $optionsResponseFactory = new OptionsResponseFactory(); $routerMiddleware = $routerMiddleware->withOptionsResponseFactory($optionsResponseFactory); ``` -or define the `Yiisoft\Router\Middleware\Router` configuration in the DI container +or define the `Yiisoft\Router\Middleware\Router` configuration in the DI container: -`config/common/di/router.php`: +`config/common/di/router.php` ```php use Yiisoft\Router\Middleware\Router; @@ -309,7 +309,7 @@ HTTP/1.1 405 Method Not Allowed Allow: GET, HEAD ``` -You can setup a custom response factory by calling `Yiisoft\Router\Middleware\Router::withNotAllowedResponseFactory()` method +You can setup a custom response factory by calling `Yiisoft\Router\Middleware\Router::withNotAllowedResponseFactory()` method: ```php use Yiisoft\Router\Middleware\Router; @@ -321,9 +321,9 @@ $notAllowedResponseFactory = new NotAllowedResponseFactory(); $routerMiddleware = $routerMiddleware->withNotAllowedResponseFactory($notAllowedResponseFactory); ``` -or define the `Yiisoft\Router\Middleware\Router` configuration in the DI container +or define the `Yiisoft\Router\Middleware\Router` configuration in the DI container: -`config/common/di/router.php`: +`config/common/di/router.php` ```php use Yiisoft\Router\Middleware\Router; diff --git a/src/MethodsResponseFactoryInterface.php b/src/MethodsResponseFactoryInterface.php index b42a5878..fc708e7e 100644 --- a/src/MethodsResponseFactoryInterface.php +++ b/src/MethodsResponseFactoryInterface.php @@ -13,7 +13,7 @@ interface MethodsResponseFactoryInterface { /** - * Handles allowed methods and produces a response. + * Produces a response listing resource's allowed methods. * * @param array $methods a list of the HTTP methods supported by the request's resource */ From 48fec84a62c08d5472d1490b084da22078f22118 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Mon, 28 Oct 2024 14:16:48 +0700 Subject: [PATCH 11/11] update tests --- tests/Middleware/RouterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Middleware/RouterTest.php b/tests/Middleware/RouterTest.php index 3f5554e4..42df4cdb 100644 --- a/tests/Middleware/RouterTest.php +++ b/tests/Middleware/RouterTest.php @@ -244,7 +244,7 @@ public function testImmutability(): void { $original = $this->createRouterMiddleware(); - $this->assertNotSame($original, $original->ignoreMethodFailureHandle()); + $this->assertNotSame($original, $original->ignoreMethodFailureHandler()); $this->assertNotSame($original, $original->withNotAllowedResponseFactory($this->createNotAllowedResponseFactory())); $this->assertNotSame($original, $original->withOptionsResponseFactory($this->createOptionsResponseFactory())); }