如何在 Symfony-CMF 中将无区域设置的 URI 重定向到全区域设置的 URI?

How should one redirect locale-less URIs to locale-full ones in Symfony-CMF?

背景

我们有一个(相当典型的?)多语言 Symfony CMF 网站安排,其中资源路径以所需语言环境为前缀——例如:

我们正在使用 RoutingAutoBundle to store such routes in the content repository, and DynamicRouter 来利用它们:简单易行。

如果 GET 请求到达时 没有 语言环境前缀,我们希望:

  1. 确定最适合用户的语言环境;然后
  2. 重定向1 用户到同一路径但添加了语言环境前缀。

当前方法

第一部分显然是 LuneticsLocaleBundle 的候选者,其猜测顺序 router 比我们期望的后备方法更高:同样,简单易行。

但是,如何最好地实施第二部分就不太明显了。目前我们已经将 Symfony 的 default/static 路由器配置为在路由链中的优先级低于 DynamicRouter,并在其中配置了一个控制器,如下所示:

/**
 * @Route("/{path}", requirements={"path" = "^(?!(en|fr)(/.*)?$)"})
 * @Method({"GET"})
 */
public function localeNotInUriAction()
{
    $request = this->getRequest();

    $this->redirect(
        '/'
      . $request->getLocale()     // set by Lunetics
      . $request->getRequestUri()
    );
}

但这感觉有点老套,我正在寻找一些东西 "cleaner"。

更好的方法?

最初我想修改 LuneticsLocaleBundle 以便在猜测者确定语言环境时它会触发一个事件,认为如果它不是 RouterLocaleGuesser 那么我们可以推断请求的 URI 不包含语言环境.然而,这显然不是这种情况,因为 RouterLocaleGuesser 只会在一开始就有路由的情况下确定语言环境——所以我不会取得任何进展。

我现在对任何其他想法都有些困惑。也许我已经在做正确的事了?如果是这样,那么我需要做的就是找到一些方法将允许的语言环境(从配置中)注入到需求正则表达式中……


  1. 外部重定向,即通过具有 HTTP 302 状态的响应。

我们使用自定义 404 处理程序和疯子:

exception_listener:
    class: AppBundle\EventListener\ExceptionListener
    arguments:
        container: "@service_container"
    tags:
        - { name:"kernel.event_listener", event:kernel.exception, handler:onKernelException }

和 php class

class ExceptionListener
{
/**
 * @var ContainerInterface
 */
protected $container;

public function __construct(ContainerInterface $container)
{
    $this->container = $container;
}

public function onKernelException(GetResponseForExceptionEvent $event)
{
    if ($this->container->getParameter('kernel.debug')) {
        // do not interfere with error handling while debugging
        return;
    }
    $exception = $event->getException();
    if ($exception instanceof NotFoundHttpException) {
        $this->handle404($event);
        return;
    }

    // ...
}

public function handle404(GetResponseForExceptionEvent $event)
{
    $request = $event->getRequest();

    if (preg_match('#^\/(de|fr|en)\/#', $request->getPathInfo())) {
        // a real 404, these are nicely handled by Twig
        return;
    }

    // i *think* that the locale is not set on the request, as lunetics comes after routing, and the routing will raise the 404
    $bestLang = $this->container->get('lunetics_locale.guesser_manager')->runLocaleGuessing($request);
    if (! $bestLang) {
        $bestLang = 'de';
    }

    $qs = $request->getQueryString();
    if (null !== $qs) {
        $qs = '?'.$qs;
    }
    $url = $request->getSchemeAndHttpHost() . $request->getBaseUrl() . '/' . $bestLang . $request->getPathInfo() . $qs;

    $this->redirect($event, $url);
}

如果还检查目标路径是否确实存在会更好 - 因为是这样,我们将 /foobar 重定向到 /de/foobar 并为那个显示 404,这不是那么优雅。