Symfony2 - 在开发模式下从请求EventListener重定向响应,同时忽略内置请求事件

Des*_*ane 6 response.redirect event-listener symfony assetic

我正在Symfony2中构建自己的用户管理系统(不使用FOSUserBundle),并希望能够强制用户更改其密码.

我已经设置了一个EventListener来监听kernal.request事件,然后我在监听器中执行一些逻辑来确定用户是否需要更改密码; 如果他们这样做,那么他们将被重定向到"更改密码"路线.

我将服务添加到我config.yml的监听kernal.request:

password_change_listener:
    class: Acme\AdminBundle\EventListener\PasswordChangeListener
        arguments: [ @service_container ]
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onMustChangepasswordEvent }
Run Code Online (Sandbox Code Playgroud)

然后是听众:

public function onMustChangepasswordEvent(GetResponseEvent $event) {

  $securityContext = $this->container->get('security.context');

  // if not logged in, no need to change password
  if ( !$securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED') )
    return;

  // If already on the change_password page, no need to change password
  $changePasswordRoute = 'change_password';
  $_route  = $event->getRequest()->get('_route');
  if ($changePasswordRoute == $_route)
    return;

  // Check the user object to see if user needs to change password
  $user = $this->getUser();
  if (!$user->getMustChangePassword())
    return;

  // If still here, redirect to the change password page
  $url = $this->container->get('router')->generate($changePasswordRoute);
  $response = new RedirectResponse($url);
  $event->setResponse($response);
}
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是在开发模式下,我的监听器还重定向探查器栏和资产请求事件.当我转储资产并清除缓存并以生产模式查看网站时,它可以正常工作.

有没有办法可以忽略来自assetic/profiler bar /任何其他内部控制器的事件?或者是将用户重定向到change_password页面的更好方法(不仅仅是登录成功)?

疯狂地思考狂野的黑客解决方案,但是在Symfony2中肯定有办法优雅地处理这个问题吗?

Des*_*ane 1

这是我现在使用的非常黑客的解决方案:

  1. 判断是否在dev环境中
  2. 如果是这样,获取所有路线的数组
  3. 过滤路由数组,以便仅保留我添加的路由
  4. 将当前路线与路线数组进行比较
  5. 如果找到匹配项,则意味着该事件不是内置控制器,但必须是我添加的控制器,因此请执行重定向。

这就是让它发挥作用的疯狂之处:

// determine if in dev environment
if (($this->container->getParameter('kernel.environment') == 'dev'))
{

  // Get array of all routes that are not built in 
  // (i.e You have added them yourself in a routing.yml file).
  // Then get the current route, and check if it exists in the array 
  $myAppName = 'Acme';
  $routes = $this->getAllNonInternalRoutes($myAppName);
  $currentRoute = $event->getRequest()->get('_route');
  if(!in_array($currentRoute, $routes))
    return;
}

// If still here, success, you have ignored the assetic and 
// web profiler actions, and any other actions that you did not add
// yourself in a routing.yml file! Go ahead and redirect!
$url = $this->container->get('router')->generate('change_password_route');
$response = new RedirectResponse($url);
$event->setResponse($response);
Run Code Online (Sandbox Code Playgroud)

getAllNonInternalRoutes()以及让它工作的疯狂黑客功能(这是我在 Qoop找到的代码的修改:

private function getAllNonInternalRoutes($app_name) {

  $router = $this->container->get('router');
  $collection = $router->getRouteCollection();
  $allRoutes = $collection->all();

  $routes = array();

  foreach ($allRoutes as $route => $params)
  {
    $defaults = $params->getDefaults();

    if (isset($defaults['_controller']))
    {
      $controllerAction = explode(':', $defaults['_controller']);
      $controller = $controllerAction[0];

      if ((strpos($controller, $app_name) === 0)) 
        $routes[]= $route;
    }
  }
  return $routes;
}
Run Code Online (Sandbox Code Playgroud)