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');
        });
});

JavaScript angularjs

酱酱酱酱。 9 years, 2 months ago

谢邀。
如果是第一次进入页面的话,为什么不在后端判断下是否是登录的;或者直接说后端可以把一些信息打印到页面上,这些信息可以是是否是登录状态。登录状态这个肯定是后端判断的。

还有就是中间可能用户还在这个页面,但是中间过程中登录token失效了,这时候需要重新登录,可以是后端对于相应的接口中返回一个固定的错误信息,例如 noLogin。然后angular中有拦截器,可以再拦截器中加入判断,如果是响应的noLogin,那么久导航到login或者有弹窗也是可以的。

xuthus answered 9 years, 2 months ago

你这个问题看似简单,但实际上有点模糊:“ 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 ),于是可以在这里做一层“登录验证保护伞”。

所以你要做两件事情:

  1. 写一个 service,内容和你放在 run 里面的代码差不多(回过头看一下你这个逻辑也还是太“蠢”了,不请求就不可能知道是否失效,不慢才怪……)
  2. 顶级抽象状态里写一个 resolve,内容就是使用这个 service

于是其他任何状态的变化都必须先 resolve 这个“保护逻辑”,这就可以做到不依赖只运行一次的 run 了。

@dolymood 你提到的第一种情况,如果是前后分离的架构,后端是没有条件往页面里写信息的;即使可以也还是无法判断登录状态是否有效,因为 SPA 不刷新的……

@M_J 理解“状态”这个概念对于写 SPA 是至关重要的,就拿本问题来说吧,你之所以卡在 “ run 只能运行一次该怎么办” 这个坑里,就是因为你没有意识到 run 和应用状态变化是无关的,它只负责应用整体的初始化逻辑。而你要的有效性检查则伴随着应用状态的变化而进行,所以你和 run 较劲就是“问道于盲”了。

芙蓉姐姐求交往 answered 9 years, 2 months ago

Your Answer