簡體   English   中英

在運行應用程序並切換到路由或狀態之前解析$ http請求

[英]Resolve $http request before running app and switching to a route or state

我編寫了一個應用程序,我需要在應用程序運行之前檢索當前登錄用戶的信息,然后再處理路由。 我使用ui-router來支持多個/嵌套視圖,並提供更豐富的有狀態路由。

當用戶登錄時,他們可以存儲表示其身份驗證令牌的cookie。 我通過調用服務來包含該令牌以檢索用戶的信息,其中包括他們所屬的組。 然后在服務中設置生成的標識,在服務中可以檢索它並在應用程序的其余部分中使用它。 更重要的是,路由器將使用該標識確保它們已登錄並屬於相應的組,然后再將其轉換為請求的狀態。

我有這樣的代碼:

app
    .config(['$stateProvider', function($stateProvider) {

        // two states; one is the protected main content, the other is the sign-in screen

        $stateProvider
            .state('main', {
                url: '/',
                data: {
                    roles: ['Customer', 'Staff', 'Admin']
                },
                views: {} // omitted
            })
            .state('account.signin', {
                url: '/signin',
                views: {} // omitted
            });
    }])
    .run(['$rootScope', '$state', '$http', 'authority', 'principal', function($rootScope, $state, $http, authority, principal) {

        $rootScope.$on('$stateChangeStart', function (event, toState) { // listen for when trying to transition states...
            var isAuthenticated = principal.isAuthenticated(); // check if the user is logged in

            if (!toState.data.roles || toState.data.roles.length == 0) return; // short circuit if the state has no role restrictions

            if (!principal.isInAnyRole(toState.data.roles)) { // checks to see what roles the principal is a member of
                event.preventDefault(); // role check failed, so...
                if (isAuthenticated) $state.go('account.accessdenied'); // tell them they are accessing restricted feature
                else $state.go('account.signin'); // or they simply aren't logged in yet
            }
        });

        $http.get('/svc/account/identity') // now, looks up the current principal
            .success(function(data) {
                authority.authorize(data); // and then stores the principal in the service (which can be injected by requiring "principal" dependency, seen above)
            }); // this does its job, but I need it to finish before responding to any routes/states
    }]);

如果我登錄,導航,退出等,這一切都按預期工作。問題是,如果我在登錄時刷新或刪除URL,我會被發送到登錄屏幕,因為身份服務呼叫沒有在州改變之前完成。 然而,在該調用完成之后,如果存在鏈接或某些東西(例如main狀態),我可以按預期繼續工作,所以我幾乎就在那里。

我知道你可以在轉換之前讓狀態等待解析參數,但我不知道如何繼續。

好吧,經過多次拔毛,這就是我想到的。

  1. 正如您所料, resolve是啟動任何異步調用並確保它們在狀態轉換之前完成的適當位置。
  2. 您將希望為確保您的resolve發生的所有狀態創建抽象父狀態,這樣如果有人刷新瀏覽器,您的異步解析仍然發生並且您的身份驗證正常工作。 您可以在state上使用parent屬性來創建另一個狀態,否則該狀態將不會被命名/點表示法繼承。 這有助於防止您的州名變得無法管理。
  3. 雖然您可以在resolve注入所需的任何服務,但您無法訪問它嘗試轉換到的狀態的toStatetoStateParams 但是, $stateChangeStart事件將在resolve之前發生。 因此,您可以將事件args中的toStatetoStateParams復制到$rootScope ,並將$rootScope注入resolve函數。 現在您可以訪問它嘗試轉換到的狀態和參數。
  4. 一旦解決了資源,就可以使用promises進行授權檢查,如果失敗,可以使用$state.go()將它們發送到登錄頁面,或者做任何你需要做的事情。 當然,有一點需要注意。
  5. 一旦在父狀態下完成resolve ,ui-router將不再解析它。 這意味着您的安全檢查不會發生! 哎呀! 解決方案是進行兩部分檢查。 一旦resolve ,我們已經討論過了。 第二次是在$stateChangeStart事件中。 這里的關鍵是檢查並查看資源是否已解決。 如果是這樣,你在做同樣的安全檢查resolve ,但在事件。 如果資源沒有得到解決,那么check in resolve將會resolve它。 要實現此目的,您需要在服務中管理資源,以便適當地管理狀態。

其他一些misc。 筆記:

  • 不要試圖將所有authz邏輯塞入$stateChangeStart 雖然您可以阻止事件並執行異步解析(在您准備好之前有效地停止更改),然后嘗試在promise承諾成功處理程序中恢復狀態更改,但是存在一些阻止其正常工作的問題。
  • 您無法更改當前狀態的onEnter方法中的狀態。

這個插件是一個工作的例子。

我們遇到了類似的問題。 我們覺得我們需要制作使HTTP調用可以訪問處理響應的邏輯的邏輯。 它們在代碼中是分開的,因此服務是一種很好的方法。

我們通過將$http.get調用包裝在緩存響應的服務中來解決該分離,並在已經填充緩存時立即調用成功回調。 例如

app.service('authorizationService', ['authority', function (authority) {
    var requestData = undefined;

    return {
        get: function (successCallback) {
            if (typeof requestData !== 'undefined') {
                successCallback(requestData);
            }
            else {
                $http.get('/svc/account/identity').success(function (data) {
                    requestData = data;
                    successCallback(data);
                });
            }
        }
    };
}]);

這可以防止請求成功。 然后,您可以安全地在$stateChangeStart處理程序中調用authorizationService.get()

如果在調用authorizationService.get()時請求已在進行中,則此方法容易受到競爭條件的影響。 有可能引入一些XHR簿記來防止這種情況發生。

或者,您可以在HTTP請求完成后發布自定義事件,並在$stateChangeStart處理程序中注冊該事件的訂閱者。 稍后您可能需要取消注冊該處理程序,可能在$stateChangeEnd處理程序中。

在授權完成之前,這些都無法完成,因此您應該通過顯示加載視圖通知用戶他們需要等待。

另外,在如何過濾路由的過程中 ,有一些關於ui-router認證的有趣討論 在AngularJS Google Group上。

我認為處理這種情況的一種更$urlRouterProvider. deferIntercept()方法是t $urlRouterProvider. deferIntercept() $urlRouterProvider. deferIntercept()$urlRouter.listen() $urlRouterProvider. deferIntercept()一起使用,以便在從服務器檢索到某些數據之前停止uiRouter。

有關詳細信息,請參閱此答案文檔

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM