我如何在 Symfony 路由中支持多个动态主机?
How can I support multiple dynamic hosts within Symfony Routing?
在我的 Symfony 应用程序 (Symfony 5.3) 中,我必须支持以下场景,其中多个 hosts/domains 属于多个应用程序上下文(即自己的防火墙和自己的控制器,有时还共享控制器):
main-domain.tld -> main_context
main-domain2.tld -> main_context
service.main-domain.tld -> service_context
service.main-domain2.tld -> service_context
service.maybe-several-other-brand-domains.tld -> service_context
admin.main-domain.tld -> admin_context
admin.main-domain2.tld -> admin_context
admin.maybe-several-other-brand-domains.tld -> admin_context
它是如何开始的
在我们有多个 brands/domains 之前,我们有 两个主要的应用上下文,它们由它们自己的主机名 寻址。所以我们做了这样的事情来将控制器分配给上下文:
#[Route(
path: '/',
requirements: ['domain' => '%app.public_hostname_context1%'],
defaults: ['domain' => '%app.public_hostname_context1%'],
host: '{domain}',
)]
# where app.public_hostname_context1 is a hostname configured in the .env.local
进展如何
这很有效,直到我们决定 多个有效主机用于其中一个上下文 ,实际上与品牌需要一样多。所以我做了一些研究并遇到了问题,我无法访问 defaults
配置中的当前主机名,因此必须在我生成的每个 url 上显式设置域。
问题是
你会如何解决这个需求?
我post我的第一个解决方法是直接回答,所以请讨论它或者用更好的方法发光。也许,我监督了一些事情,我有一种轻微的感觉,即该解决方案可能不是最好的。对于遇到相同要求的其他人,整个问题将记录至少一种解决方法。 :)
首先,从路由定义中删除 defaults
并为上下文的几个有效域提供模式:
#[Route(
path: '/',
requirements: ['domain' => '%app.public_hostnames_context1_pattern%'],
host: '{domain}',
)]
# app.public_hostname_context1_pattern is a pattern configured in the .env.local
# containing all possible hostnames for that context like
# PUBLIC_HOSTNAME_CONTEXT1_PATTERN=(?:service\.main-domain\.tld|service\.main-domain2\.tld)
要将当前主机名设置为所有路由的域参数的默认值,我有一个 2012 年的 RequestListener inspired by this answer 设置它,然后 RouterListener 开始工作。
在我的 services.yaml:
# must be called before the RouterListener (with priority 32) to load the domain
App\EventListener\RequestListener:
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 33 }
和 RequestListener:
<?php
declare(strict_types=1);
namespace App\EventListener;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\Routing\RouterInterface;
class RequestListener
{
public function __construct(
private RouterInterface $router,
){}
public function onKernelRequest(RequestEvent $event)
{
if (false === $this->router->getContext()->hasParameter('domain')) {
$this->router->getContext()->setParameter('domain', $event->getRequest()->getHost());
}
}
}
这样做的好处是,我仍然可以在创建 URL 时覆盖 domain
参数。但是我看到的一个缺点是:当我为另一个上下文生成 URL 并且没有明确设置域时,会引发错误,因为现在当前请求的主机用作另一个域上下文,这是要求中的模式所不允许的。我可以忍受这一点。你呢?
在我的 Symfony 应用程序 (Symfony 5.3) 中,我必须支持以下场景,其中多个 hosts/domains 属于多个应用程序上下文(即自己的防火墙和自己的控制器,有时还共享控制器):
main-domain.tld -> main_context
main-domain2.tld -> main_context
service.main-domain.tld -> service_context
service.main-domain2.tld -> service_context
service.maybe-several-other-brand-domains.tld -> service_context
admin.main-domain.tld -> admin_context
admin.main-domain2.tld -> admin_context
admin.maybe-several-other-brand-domains.tld -> admin_context
它是如何开始的
在我们有多个 brands/domains 之前,我们有 两个主要的应用上下文,它们由它们自己的主机名 寻址。所以我们做了这样的事情来将控制器分配给上下文:
#[Route(
path: '/',
requirements: ['domain' => '%app.public_hostname_context1%'],
defaults: ['domain' => '%app.public_hostname_context1%'],
host: '{domain}',
)]
# where app.public_hostname_context1 is a hostname configured in the .env.local
进展如何
这很有效,直到我们决定 多个有效主机用于其中一个上下文 ,实际上与品牌需要一样多。所以我做了一些研究并遇到了问题,我无法访问 defaults
配置中的当前主机名,因此必须在我生成的每个 url 上显式设置域。
问题是
你会如何解决这个需求?
我post我的第一个解决方法是直接回答,所以请讨论它或者用更好的方法发光。也许,我监督了一些事情,我有一种轻微的感觉,即该解决方案可能不是最好的。对于遇到相同要求的其他人,整个问题将记录至少一种解决方法。 :)
首先,从路由定义中删除 defaults
并为上下文的几个有效域提供模式:
#[Route(
path: '/',
requirements: ['domain' => '%app.public_hostnames_context1_pattern%'],
host: '{domain}',
)]
# app.public_hostname_context1_pattern is a pattern configured in the .env.local
# containing all possible hostnames for that context like
# PUBLIC_HOSTNAME_CONTEXT1_PATTERN=(?:service\.main-domain\.tld|service\.main-domain2\.tld)
要将当前主机名设置为所有路由的域参数的默认值,我有一个 2012 年的 RequestListener inspired by this answer 设置它,然后 RouterListener 开始工作。
在我的 services.yaml:
# must be called before the RouterListener (with priority 32) to load the domain
App\EventListener\RequestListener:
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 33 }
和 RequestListener:
<?php
declare(strict_types=1);
namespace App\EventListener;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\Routing\RouterInterface;
class RequestListener
{
public function __construct(
private RouterInterface $router,
){}
public function onKernelRequest(RequestEvent $event)
{
if (false === $this->router->getContext()->hasParameter('domain')) {
$this->router->getContext()->setParameter('domain', $event->getRequest()->getHost());
}
}
}
这样做的好处是,我仍然可以在创建 URL 时覆盖 domain
参数。但是我看到的一个缺点是:当我为另一个上下文生成 URL 并且没有明确设置域时,会引发错误,因为现在当前请求的主机用作另一个域上下文,这是要求中的模式所不允许的。我可以忍受这一点。你呢?