2

我正在开发一个带有 RoR 后端的 AngularJS 应用程序,但在使用多个布局时遇到了问题。应用程序在将页面呈现给未经身份验证的用户时使用一种布局,并在用户通过身份验证后更改为另一种布局。

布局是初始页面加载时的服务器,由 Rails 管理。一些示例代码说明了我们如何根据路线加载不同的布局:

class SampleController < ApplicationController
  layout :current_layout

  def current_layout
    "layout" unless request.xhr?
  end
end

不同部分的示例控制器:

class SampleController2 < ApplicationController
  layout :current_layout

  def current_layout
    "anotherLayout" unless request.xhr?
  end
end

这是为管理经过身份验证/未经身份验证的用户的控制器单独定义的,并且基本上提供了正确的布局。当 Angular 出现时,我们使用 XHR 检查来防止路由循环。

所以这在大多数浏览器中都可以正常工作,但在使用 IE9 时会中断。Angular 回退到使用 #! IE9 中的 URL,因此 Rails 不知道要加载哪个控制器,因为哈希不会发送到后端。在这种情况下,Rails 会加载根及其关联的布局。如果已验证部分设置为默认值,则即使对于未经验证的用户,它也会加载此布局,反之亦然。

所以基本上,我需要找到一种方法让这个多布局应用程序即使在不支持 HTML5 pushState 的浏览器中也能正常工作。我已经到处寻找合适的解决方案,但还没有想出任何办法。

4

1 回答 1

2

好的,所以在客户端和服务器端测试了大量的调整之后,我最终使用了以下解决方案。基本上,我只是停止将 Angular 暴露给多种布局,并在 Rails 中处理了它。当然,这确实对用户施加了限制。

该应用程序分为两个主要部分:仪表板身份验证。因此,在后端,我们限制用户在登录后访问任何身份验证页面,显然只有在用户通过身份验证后才能访问仪表板页面。

问题的根源在于 Rails 无法知道要提供哪个布局,如果应该提供一个布局。我们使用 cookie 来区分经过身份验证和未经身份验证的用户。如果设置了 cookie,并且它通过了真实性测试,那么应该加载仪表板布局。否则,任何尝试访问仪表板的用户都将被重定向到身份验证部分:

class DashboardController < ApplicationController

  layout :current_layout

  before_filter :authenticate_user

  def authenticate_user
    if request.xhr? && !cookies[:access_token]
      redirect_to "/login"
    end
  end

  def current_layout
    if cookies[:access_token]
      "dashboard" unless request.xhr?
    else 
      "application" unless request.xhr?
    end
  end
end

对于身份验证部分也是如此:

class AuthenticationController < ApplicationController

  layout :current_layout

  before_filter :redirect_if_authenticated

  def redirect_if_authenticated
    if request.xhr? && cookies[:access_token]
      redirect_to "/dashboard"
    end
  end

  def current_layout
    if cookies[:access_token]
      "dashboard" unless request.xhr?
    else 
      "application" unless request.xhr?
    end
  end
end

所以这里要注意的要点:

  • 如果请求是 XHR,则不要再次提供布局。再次提供布局将导致 IE9 中的无限加载循环,可能在其他 IE 版本中也是如此。

  • 由于我们没有在服务器端获取 URL 片段,因此我们无法知道应该加载哪个布局。因此,我们使用 cookie 作为事实来源,并且仅基于此来控制访问。

  • 这在客户端引入了一些不对称性:在某些情况下,如果用户键入另一个部分的 URL,则 url 和布局将保留。由于我们无法根据来自服务器的 URL 做任何事情,而且在这种情况下 cookie 保持不变,这必须由 Angular 处理。

最后一点,在我的案例中,身份验证部分只有几个可能的 url,所以如果我得到一个正则表达式匹配,我只是重定向如下:

if(Modernizr.history) {
    if(!/(login|sign|pass)/.test(location.pathname)) {
        $location.path('/');
    }
} else {
    if(location.hash.length > 3 && !/(login|sign|pass)/.test(location.hash)) {
        $window.location.href = '/';
    }
}

在这里检查historyAPI,因为在较旧的 IE 浏览器中(我猜在其他较旧的浏览器中也是如此?),$location.path('/')并没有一致地重新加载整个页面。使用它时,$routeProvider配置还应该有条件地加载布局,对于我们重定向到 root 的情况/

$routeProvider
// Code for routes
.otherwise({
  // Of course, add something to check access_token authenticity here as well :P
  redirectTo: $.cookie('access_token') ? "/dashboard" : "/login"
});

因此,通过这些调整,该应用程序可以在所有浏览器中正常运行,并且可以在多种布局下运行。当然,我对 RoR 一无所知,所以我希望有人能提出一些改进建议或更好的答案。;)

于 2013-07-30T06:36:50.653 回答