好的,所以在客户端和服务器端测试了大量的调整之后,我最终使用了以下解决方案。基本上,我只是停止将 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 = '/';
}
}
在这里检查history
API,因为在较旧的 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 一无所知,所以我希望有人能提出一些改进建议或更好的答案。;)