Skip to content

Commit 0918555

Browse files
authored
feat(router): add ability to skip middleware per route (#1472)
1 parent 53ea4fc commit 0918555

File tree

17 files changed

+116
-10
lines changed

17 files changed

+116
-10
lines changed

docs/1-essentials/01-routing.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ Note that priority is defined using an integer. You can however use one of the b
374374

375375
### Middleware discovery
376376

377-
Global middleware classes are discovered and sorted based on their priority. You can make a middleware class non-global by adding the `#[SkipDiscovery]` attribute:
377+
Global middleware classes are discovered and sorted based on their priority. You can make a middleware class non-global by adding the {b`#[Tempest\Discovery\SkipDiscovery]`} attribute:
378378

379379
```php
380380
use Tempest\Discovery\SkipDiscovery;
@@ -384,6 +384,48 @@ final readonly class ValidateWebhook implements HttpMiddleware
384384
{ /* … */ }
385385
```
386386

387+
### Excluding route middleware
388+
389+
Some routes may not require specific global middleware to be applied. For instance, API routes do not need CSRF protection. You may skip specific middleware by using the `without` argument of the route attribute.
390+
391+
```php app/Slack/ReceiveInteractionController.php
392+
use Tempest\Router\Post;
393+
use Tempest\Http\Response;
394+
395+
final readonly class ReceiveInteractionController
396+
{
397+
#[Post('/slack/interaction', without: [VerifyCsrfMiddleware::class, SetCookieMiddleware::class])]
398+
public function __invoke(): Response
399+
{
400+
// …
401+
}
402+
}
403+
```
404+
405+
### Group middleware
406+
407+
While Tempest does not provide a way to group middleware, you can easily create your own route attribute that applies or excludes a set of middleware to a route.
408+
409+
```php Api.php
410+
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
411+
final readonly class Api implements Route
412+
{
413+
public function __construct(
414+
public Method $method,
415+
public string $uri,
416+
public array $middleware = [],
417+
public array $without = [],
418+
) {
419+
$this->uri = "/api/{$uri}";
420+
$this->without[] = [
421+
...$without,
422+
VerifyCsrfMiddleware::class,
423+
SetCookieMiddleware::class
424+
];
425+
}
426+
}
427+
```
428+
387429
## Responses
388430

389431
All requests to a controller action expect a response to be returned to the client. This is done by returning a `{php}View` or a `{php}Response` object.

packages/router/src/Connect.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
public Method $method;
1414

1515
/**
16-
* @param class-string<HttpMiddleware>[] $middleware
16+
* @param class-string<HttpMiddleware>[] $middleware Middleware specific to this route.
17+
* @param class-string<HttpMiddleware>[] $without Middleware to remove from this route.
1718
*/
1819
public function __construct(
1920
public string $uri,
2021
public array $middleware = [],
22+
public array $without = [],
2123
) {
2224
$this->method = Method::CONNECT;
2325
}

packages/router/src/Delete.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
public Method $method;
1414

1515
/**
16-
* @param class-string<HttpMiddleware>[] $middleware
16+
* @param class-string<HttpMiddleware>[] $middleware Middleware specific to this route.
17+
* @param class-string<HttpMiddleware>[] $without Middleware to remove from this route.
1718
*/
1819
public function __construct(
1920
public string $uri,
2021
public array $middleware = [],
22+
public array $without = [],
2123
) {
2224
$this->method = Method::DELETE;
2325
}

packages/router/src/GenericRouter.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ private function getCallable(): HttpMiddlewareCallable
7777

7878
foreach ($middlewareStack->unwrap() as $middlewareClass) {
7979
$callable = new HttpMiddlewareCallable(function (Request $request) use ($middlewareClass, $callable) {
80+
// We skip this middleware if it's ignored by the route
81+
if ($this->container->has(MatchedRoute::class)) {
82+
$matchedRoute = $this->container->get(MatchedRoute::class);
83+
84+
if (in_array($middlewareClass->getName(), $matchedRoute->route->without, strict: true)) {
85+
return $callable($request);
86+
}
87+
}
88+
8089
/** @var HttpMiddleware $middleware */
8190
$middleware = $this->container->get($middlewareClass->getName());
8291

packages/router/src/Get.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
public Method $method;
1414

1515
/**
16-
* @param class-string<HttpMiddleware>[] $middleware
16+
* @param class-string<HttpMiddleware>[] $middleware Middleware specific to this route.
17+
* @param class-string<HttpMiddleware>[] $without Middleware to remove from this route.
1718
*/
1819
public function __construct(
1920
public string $uri,
2021
public array $middleware = [],
22+
public array $without = [],
2123
) {
2224
$this->method = Method::GET;
2325
}

packages/router/src/Head.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
public Method $method;
1414

1515
/**
16-
* @param class-string<HttpMiddleware>[] $middleware
16+
* @param class-string<HttpMiddleware>[] $middleware Middleware specific to this route.
17+
* @param class-string<HttpMiddleware>[] $without Middleware to remove from this route.
1718
*/
1819
public function __construct(
1920
public string $uri,
2021
public array $middleware = [],
22+
public array $without = [],
2123
) {
2224
$this->method = Method::HEAD;
2325
}

packages/router/src/Options.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
public Method $method;
1414

1515
/**
16-
* @param class-string<HttpMiddleware>[] $middleware
16+
* @param class-string<HttpMiddleware>[] $middleware Middleware specific to this route.
17+
* @param class-string<HttpMiddleware>[] $without Middleware to remove from this route.
1718
*/
1819
public function __construct(
1920
public string $uri,
2021
public array $middleware = [],
22+
public array $without = [],
2123
) {
2224
$this->method = Method::OPTIONS;
2325
}

packages/router/src/Patch.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
public Method $method;
1414

1515
/**
16-
* @param class-string<HttpMiddleware>[] $middleware
16+
* @param class-string<HttpMiddleware>[] $middleware Middleware specific to this route.
17+
* @param class-string<HttpMiddleware>[] $without Middleware to remove from this route.
1718
*/
1819
public function __construct(
1920
public string $uri,
2021
public array $middleware = [],
22+
public array $without = [],
2123
) {
2224
$this->method = Method::PATCH;
2325
}

packages/router/src/Post.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
public Method $method;
1414

1515
/**
16-
* @param class-string<HttpMiddleware>[] $middleware
16+
* @param class-string<HttpMiddleware>[] $middleware Middleware specific to this route.
17+
* @param class-string<HttpMiddleware>[] $without Middleware to remove from this route.
1718
*/
1819
public function __construct(
1920
public string $uri,
2021
public array $middleware = [],
22+
public array $without = [],
2123
) {
2224
$this->method = Method::POST;
2325
}

packages/router/src/Put.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
public Method $method;
1414

1515
/**
16-
* @param class-string<HttpMiddleware>[] $middleware
16+
* @param class-string<HttpMiddleware>[] $middleware Middleware specific to this route.
17+
* @param class-string<HttpMiddleware>[] $without Middleware to remove from this route.
1718
*/
1819
public function __construct(
1920
public string $uri,
2021
public array $middleware = [],
22+
public array $without = [],
2123
) {
2224
$this->method = Method::PUT;
2325
}

0 commit comments

Comments
 (0)