Skip to content

Commit 37e1823

Browse files
feature #39688 [FrameworkBundle][Messenger] Added RouterContextMiddleware (jderusse)
This PR was merged into the 5.3-dev branch. Discussion ---------- [FrameworkBundle][Messenger] Added RouterContextMiddleware | Q | A | ------------- | --- | Branch? | 5.x | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | TODO When handling a message in async, we, sometimes need the Router Context to generate absolute URL. ie: - sending an email when the message contains only the template - generating a PDF People can use the configuration `router.default_uri` to workaround and fix the issue, but this does not work when the web application servers several domains. This PR provide a new middleware that store the current router context in a stamp, and restore the context when processing the message. Commits ------- 8fe8b96 [Messenger] Added RouterContextMiddleware
2 parents bbc2d6a + 8fe8b96 commit 37e1823

File tree

7 files changed

+227
-0
lines changed

7 files changed

+227
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
9898
use Symfony\Component\Messenger\MessageBus;
9999
use Symfony\Component\Messenger\MessageBusInterface;
100+
use Symfony\Component\Messenger\Middleware\RouterContextMiddleware;
100101
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
101102
use Symfony\Component\Messenger\Transport\TransportFactoryInterface;
102103
use Symfony\Component\Messenger\Transport\TransportInterface;
@@ -944,9 +945,13 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co
944945
if (!$this->isConfigEnabled($container, $config)) {
945946
$container->removeDefinition('console.command.router_debug');
946947
$container->removeDefinition('console.command.router_match');
948+
$container->removeDefinition('messenger.middleware.router_context');
947949

948950
return;
949951
}
952+
if (!class_exists(RouterContextMiddleware::class)) {
953+
$container->removeDefinition('messenger.middleware.router_context');
954+
}
950955

951956
$loader->load('routing.php');
952957

src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Symfony\Component\Messenger\Middleware\FailedMessageProcessingMiddleware;
2727
use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware;
2828
use Symfony\Component\Messenger\Middleware\RejectRedeliveredMessageMiddleware;
29+
use Symfony\Component\Messenger\Middleware\RouterContextMiddleware;
2930
use Symfony\Component\Messenger\Middleware\SendMessageMiddleware;
3031
use Symfony\Component\Messenger\Middleware\TraceableMiddleware;
3132
use Symfony\Component\Messenger\Middleware\ValidationMiddleware;
@@ -100,6 +101,11 @@
100101
service('debug.stopwatch'),
101102
])
102103

104+
->set('messenger.middleware.router_context', RouterContextMiddleware::class)
105+
->args([
106+
service('router'),
107+
])
108+
103109
// Discovery
104110
->set('messenger.receiver_locator')
105111
->args([

src/Symfony/Component/Messenger/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
5.3
55
---
66

7+
* Add the `RouterContextMiddleware` to restore the original router context when handling a message
78
* `InMemoryTransport` can perform message serialization through dsn `in-memory://?serialize=true`.
89
* Added `queues` option to `Worker` to only fetch messages from a specific queue from a receiver implementing `QueueReceiverInterface`.
910

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\Middleware;
13+
14+
use Symfony\Component\Messenger\Envelope;
15+
use Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp;
16+
use Symfony\Component\Messenger\Stamp\RouterContextStamp;
17+
use Symfony\Component\Routing\RequestContext;
18+
use Symfony\Component\Routing\RequestContextAwareInterface;
19+
20+
/**
21+
* Restore the Router context when processing the message.
22+
*
23+
* @author Jérémy Derussé <jeremy@derusse.com>
24+
*/
25+
class RouterContextMiddleware implements MiddlewareInterface
26+
{
27+
private $router;
28+
29+
public function __construct(RequestContextAwareInterface $router)
30+
{
31+
$this->router = $router;
32+
}
33+
34+
public function handle(Envelope $envelope, StackInterface $stack): Envelope
35+
{
36+
if (!$envelope->last(ConsumedByWorkerStamp::class) || !$contextStamp = $envelope->last(RouterContextStamp::class)) {
37+
$context = $this->router->getContext();
38+
$envelope = $envelope->with(new RouterContextStamp(
39+
$context->getBaseUrl(),
40+
$context->getMethod(),
41+
$context->getHost(),
42+
$context->getScheme(),
43+
$context->getHttpPort(),
44+
$context->getHttpsPort(),
45+
$context->getPathInfo(),
46+
$context->getQueryString()
47+
));
48+
49+
return $stack->next()->handle($envelope, $stack);
50+
}
51+
52+
$currentContext = $this->router->getContext();
53+
54+
/* @var RouterContextStamp $contextStamp */
55+
$this->router->setContext(new RequestContext(
56+
$contextStamp->getBaseUrl(),
57+
$contextStamp->getMethod(),
58+
$contextStamp->getHost(),
59+
$contextStamp->getScheme(),
60+
$contextStamp->getHttpPort(),
61+
$contextStamp->getHttpsPort(),
62+
$contextStamp->getPathInfo(),
63+
$contextStamp->getQueryString()
64+
));
65+
66+
try {
67+
return $stack->next()->handle($envelope, $stack);
68+
} finally {
69+
$this->router->setContext($currentContext);
70+
}
71+
}
72+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\Stamp;
13+
14+
/**
15+
* @author Jérémy Derussé <jeremy@derusse.com>
16+
*/
17+
class RouterContextStamp implements StampInterface
18+
{
19+
private $baseUrl;
20+
private $method;
21+
private $host;
22+
private $scheme;
23+
private $httpPort;
24+
private $httpsPort;
25+
private $pathInfo;
26+
private $queryString;
27+
28+
public function __construct(string $baseUrl, string $method, string $host, string $scheme, int $httpPort, int $httpsPort, string $pathInfo, string $queryString)
29+
{
30+
$this->baseUrl = $baseUrl;
31+
$this->method = $method;
32+
$this->host = $host;
33+
$this->scheme = $scheme;
34+
$this->httpPort = $httpPort;
35+
$this->httpsPort = $httpsPort;
36+
$this->pathInfo = $pathInfo;
37+
$this->queryString = $queryString;
38+
}
39+
40+
public function getBaseUrl(): string
41+
{
42+
return $this->baseUrl;
43+
}
44+
45+
public function getMethod(): string
46+
{
47+
return $this->method;
48+
}
49+
50+
public function getHost(): string
51+
{
52+
return $this->host;
53+
}
54+
55+
public function getScheme(): string
56+
{
57+
return $this->scheme;
58+
}
59+
60+
public function getHttpPort(): int
61+
{
62+
return $this->httpPort;
63+
}
64+
65+
public function getHttpsPort(): int
66+
{
67+
return $this->httpsPort;
68+
}
69+
70+
public function getPathInfo(): string
71+
{
72+
return $this->pathInfo;
73+
}
74+
75+
public function getQueryString(): string
76+
{
77+
return $this->queryString;
78+
}
79+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace Symfony\Component\Messenger\Tests\Middleware;
4+
5+
use Symfony\Component\Messenger\Envelope;
6+
use Symfony\Component\Messenger\Middleware\RouterContextMiddleware;
7+
use Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp;
8+
use Symfony\Component\Messenger\Stamp\RouterContextStamp;
9+
use Symfony\Component\Messenger\Test\Middleware\MiddlewareTestCase;
10+
use Symfony\Component\Routing\RequestContext;
11+
use Symfony\Component\Routing\RequestContextAwareInterface;
12+
13+
class RouterContextMiddlewareTest extends MiddlewareTestCase
14+
{
15+
public function testMiddlewareStoreContext()
16+
{
17+
$context = new RequestContext('/', 'GET', 'symfony.com');
18+
19+
$router = $this->createMock(RequestContextAwareInterface::class);
20+
$router
21+
->expects($this->once())
22+
->method('getContext')
23+
->willReturn($context);
24+
25+
$middleware = new RouterContextMiddleware($router);
26+
27+
$envelope = new Envelope(new \stdClass());
28+
$envelope = $middleware->handle($envelope, $this->getStackMock());
29+
30+
$this->assertNotNull($stamp = $envelope->last(RouterContextStamp::class));
31+
$this->assertSame('symfony.com', $stamp->getHost());
32+
}
33+
34+
public function testMiddlewareRestoreContext()
35+
{
36+
$router = $this->createMock(RequestContextAwareInterface::class);
37+
$originalContext = new RequestContext();
38+
39+
$router
40+
->expects($this->once())
41+
->method('getContext')
42+
->willReturn($originalContext);
43+
44+
$router
45+
->expects($this->exactly(2))
46+
->method('setContext')
47+
->withConsecutive(
48+
[$this->callback(function ($context) {
49+
$this->assertSame('symfony.com', $context->getHost());
50+
51+
return true;
52+
})],
53+
[$originalContext]
54+
);
55+
56+
$middleware = new RouterContextMiddleware($router);
57+
$envelope = new Envelope(new \stdClass(), [
58+
new ConsumedByWorkerStamp(),
59+
new RouterContextStamp('', 'GET', 'symfony.com', 'https', 80, 443, '/', ''),
60+
]);
61+
$middleware->handle($envelope, $this->getStackMock());
62+
}
63+
}

src/Symfony/Component/Messenger/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"symfony/http-kernel": "^4.4|^5.0",
3333
"symfony/process": "^4.4|^5.0",
3434
"symfony/property-access": "^4.4|^5.0",
35+
"symfony/routing": "^4.4|^5.0",
3536
"symfony/serializer": "^4.4|^5.0",
3637
"symfony/service-contracts": "^1.1|^2",
3738
"symfony/stopwatch": "^4.4|^5.0",

0 commit comments

Comments
 (0)