Skip to content

Commit 2deb76a

Browse files
committed
feat(routing): make request path decoding configurable
1 parent dbb6a71 commit 2deb76a

3 files changed

Lines changed: 45 additions & 4 deletions

File tree

Slim/App.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Psr\Http\Server\MiddlewareInterface;
1717
use Psr\Http\Server\RequestHandlerInterface;
1818
use Psr\Log\LoggerInterface;
19+
use Slim\Interfaces\DispatcherInterface;
1920
use Slim\Interfaces\EmitterInterface;
2021
use Slim\Interfaces\RouterInterface;
2122
use Slim\Interfaces\ServerRequestCreatorInterface;
@@ -169,12 +170,23 @@ public function addMiddleware(MiddlewareInterface $middleware): self
169170
/**
170171
* Add routing middleware.
171172
*
173+
* @param bool $decodePath Whether the request path should be URL-decoded before dispatch.
174+
* Disable to preserve encoded reserved characters inside route parameters.
175+
*
172176
* @return self
173177
*/
174-
public function addRoutingMiddleware(): self
178+
public function addRoutingMiddleware(bool $decodePath = true): self
175179
{
180+
$routingMiddleware = $decodePath
181+
? RoutingMiddleware::class
182+
: new RoutingMiddleware(
183+
$this->container->get(DispatcherInterface::class),
184+
$this->router,
185+
false,
186+
);
187+
176188
return $this
177-
->add(RoutingMiddleware::class)
189+
->add($routingMiddleware)
178190
->add(EndpointMiddleware::class);
179191
}
180192

Slim/Middleware/RoutingMiddleware.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,16 @@ final class RoutingMiddleware implements MiddlewareInterface
3030

3131
private RouterInterface $router;
3232

33+
private bool $decodePath;
34+
3335
public function __construct(
3436
DispatcherInterface $dispatcher,
35-
RouterInterface $router
37+
RouterInterface $router,
38+
bool $decodePath = true
3639
) {
3740
$this->dispatcher = $dispatcher;
3841
$this->router = $router;
42+
$this->decodePath = $decodePath;
3943
}
4044

4145
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
@@ -46,7 +50,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
4650

4751
$routingResult = $this->dispatcher->dispatch(
4852
$request->getMethod(),
49-
rawurldecode($dispatchPath)
53+
$this->decodePath ? rawurldecode($dispatchPath) : $dispatchPath
5054
);
5155

5256
$routeMatch = $this->createRouteMatch($routingResult);

tests/Middleware/RoutingMiddlewareTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,31 @@ public function testRoutingWithBasePath(): void
198198
$this->assertSame('/api/users/123?page=2', $response->getHeaderLine('X-fullUrlFor'));
199199
}
200200

201+
public function testRoutePreservesEncodedReservedCharactersWhenPathDecodingDisabled(): void
202+
{
203+
$app = AppFactory::create();
204+
$app->addRoutingMiddleware(false);
205+
206+
$app->get('/something/{magic}/{foo}', function (
207+
ServerRequestInterface $request,
208+
ResponseInterface $response,
209+
array $args
210+
) {
211+
$response->getBody()->write($args['magic'] . '|' . $args['foo']);
212+
213+
return $response;
214+
});
215+
216+
$request = $this
217+
->getServerRequestFactory($app)
218+
->createServerRequest('GET', '/something/magic/foo%2Fbar');
219+
220+
$response = $app->handle($request);
221+
222+
$this->assertSame(200, $response->getStatusCode());
223+
$this->assertSame('magic|foo%2Fbar', (string)$response->getBody());
224+
}
225+
201226
public function testMethodNotAllowedThrowsRuntimeExceptionWhenAllowedMethodsPayloadIsInvalid(): void
202227
{
203228
$dispatcher = $this->createMock(DispatcherInterface::class);

0 commit comments

Comments
 (0)