在Symfony CMF中,应该如何将无区域设置的URI重定向到区域设置完整的URI


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

背景

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

  • http://www.example.com/en/path/to/english-resource.html;以及
  • CCD_ 2

我们使用RoutingAutoBundle将此类路由存储在内容存储库中,并使用DynamicRouter来利用它们:简单易用。

如果GET请求到达时没有区域设置前缀,我们希望:

  1. 确定最适合用户的区域设置;然后
  2. 1用户重定向到相同的路径,但添加了区域设置前缀

当前方法

第一部分显然是LuneticsLocaleBundle的候选者,router的猜测顺序高于我们想要的回退方法:同样,简单易行。

然而,如何最好地实现第二部分就不那么明显了。目前,我们已将Symfony的默认/静态路由器配置为在路由链中的优先级低于DynamicRouter,并在其中配置了如下控制器:

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

但这让我觉得很生气,我正在寻找"更干净"的东西。

更好的方法

最初,我想修改LuneticsLocaleBundle,这样每当猜测者确定区域设置时,它就会触发一个事件,认为如果不是RouterLocaleGuesser,那么我们可以推断请求的URI不包含区域设置。然而,事实显然并非如此,因为RouterLocaleGuesser只会在一开始就有路由的情况下确定区域设置,所以我不会取得任何进展。

我现在有点纠结于其他的想法。也许我已经在做正确的事情了?如果是这样,那么我所需要做的就是找到一些方法将允许的区域设置(从配置(注入到需求regex…


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

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

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

和php类

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,这并不那么优雅。