我如何在 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 并且没有明确设置域时,会引发错误,因为现在当前请求的主机用作另一个域上下文,这是要求中的模式所不允许的。我可以忍受这一点。你呢?