AngularJS 应用请求设置同步问题~
要求:整个应用需要用户登录之后才进入,所以我在app.run 的时候与服务器交互,判断是否登录,并设置一些用户信息,因 $http 都是异步的,导致返回的不及时,网上百度了一些方法,
大多数都是 $q 服务,现在问题是 主要是 app.run 只运行一次,用$q 该如何操作~ 求大神求解
javascript
app.run(function($rootScope, $state, $stateParams, $http, $filter) { $rootScope.$state = $state; $rootScope.$stateParams = $stateParams; // 用户登录判断以及所需信息 $http.get(app.URL.login). success(function(data) { if (data.code > 0 && data.isLogin) { $rootScope.nowDate = { year: parseInt($filter('date')(data.nowDate, 'yyyy'), 10), month: parseInt($filter('date')(data.nowDate, 'M'), 10), day: parseInt($filter('date')(data.nowDate, 'd'), 10) }; $rootScope.userInfo = { isLogin: data.isLogin, userName: data.userName, avatar: data.avatar }; } else { $state.go('login'); } }). error(function() { $state.go('login'); }); });
Answers
你这个问题看似简单,但实际上有点模糊:“
run
只运行一次”这个先觉条件到底有什么影响呢?
看了半天我才明白你真正的问题在于你要知道用户的登录状态是否还有效,因为 SPA 应用不需要刷新页面,所以你的
run
只有运行一次的机会,因而如果用户不刷新浏览器且使用了很长时间(可能超过了 session 的有效期)那么你无法判断用户的登录还是否有效了。我说的没错吧?
实际上你这个问题根源不在前端,而在后端的 API 那里。如果你是 session 验证且会过期失效,那么失效与否不应该是用一个固定的 API Endpoint(也就是你例子里的
app.URL.login
)来检查,而是应该在每一次 API 请求里都检查,并且如果 session 失效就返回给客户端一个特定的信息。
接下来就像
@dolymood
说的那样,在客户端用拦截器预先做好对此特定信息的处理,一旦收到就清空本地缓存的登录和用户信息然后跳转至 login。这才是解决你的问题的根本之道,和用不用
$q
以及
run
能否运行多次没有任何关系。
另外如有可能,还可以把 session 验证机制换成 token 验证机制,它好处会更多一些,特别是可以避免 cookie 带来的一些烦恼。不过 token 验证判断是否失效的道理和过程也和上述类似,无论如何后端 API 总是要反馈给客户端必要的信息的,不是通过固定的接口去“拉”这个信息,而是访问任何接口的时候都把信息“推”给客户端。
如果你完全触碰不到后端,只有一个固定的 API 来帮你判断登录状态的话(好可怜),那么到还有一个方法可以让你摆脱
run
只运行一次的困扰。不过先把它的缺陷说在前头:这个机制是依赖客户端应用的状态变更的!如果你的应用里有无须状态变更就可以请求 API 的业务逻辑,那就会出现“漏网之鱼”。什么意思?比如说你的页面里有很多链接/按钮,点击它们不会改变应用的状态而是直接发起异步请求,那就没有检查登录失效的先决条件了。除非你干脆写个拦截器把所有的 HTTP 请求都加一层登录失效检查,但这样也太蠢了……
OK,说正事:
看你的代码,你应该是用了 ui-router 的,那么你是否知道 ui-router 有一个
abstract state
机制可以利用一下?细节不多说了,自己看 ui-router 的文档,反正如果你写了一个顶级的
abstract state
就意味着其他任何状态的变更都会“继承”这个顶级抽象状态的逻辑定义。这样的 state 有点类似于
run
(但作用领域是不同的,我倾向于把和业务逻辑无关的但是需要在进入应用之前的动作写在 run 里,而把和业务逻辑相关的,会频繁“动作”的行为放入
abstract state
),于是可以在这里做一层“登录验证保护伞”。
所以你要做两件事情:
-
写一个 service,内容和你放在
run
里面的代码差不多(回过头看一下你这个逻辑也还是太“蠢”了,不请求就不可能知道是否失效,不慢才怪……) - 顶级抽象状态里写一个 resolve,内容就是使用这个 service
于是其他任何状态的变化都必须先 resolve 这个“保护逻辑”,这就可以做到不依赖只运行一次的
run
了。
@dolymood 你提到的第一种情况,如果是前后分离的架构,后端是没有条件往页面里写信息的;即使可以也还是无法判断登录状态是否有效,因为 SPA 不刷新的……
@M_J
理解“状态”这个概念对于写 SPA 是至关重要的,就拿本问题来说吧,你之所以卡在 “
run
只能运行一次该怎么办” 这个坑里,就是因为你没有意识到
run
和应用状态变化是无关的,它只负责应用整体的初始化逻辑。而你要的有效性检查则伴随着应用状态的变化而进行,所以你和
run
较劲就是“问道于盲”了。