[英]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
狀態),我可以按預期繼續工作,所以我幾乎就在那里。
我知道你可以在轉換之前讓狀態等待解析參數,但我不知道如何繼續。
好吧,經過多次拔毛,這就是我想到的。
resolve
是啟動任何異步調用並確保它們在狀態轉換之前完成的適當位置。 resolve
發生的所有狀態創建抽象父狀態,這樣如果有人刷新瀏覽器,您的異步解析仍然發生並且您的身份驗證正常工作。 您可以在state
上使用parent
屬性來創建另一個狀態,否則該狀態將不會被命名/點表示法繼承。 這有助於防止您的州名變得無法管理。 resolve
注入所需的任何服務,但您無法訪問它嘗試轉換到的狀態的toState
或toStateParams
。 但是, $stateChangeStart
事件將在resolve
之前發生。 因此,您可以將事件args中的toState
和toStateParams
復制到$rootScope
,並將$rootScope
注入resolve
函數。 現在您可以訪問它嘗試轉換到的狀態和參數。 $state.go()
將它們發送到登錄頁面,或者做任何你需要做的事情。 當然,有一點需要注意。 resolve
,ui-router將不再解析它。 這意味着您的安全檢查不會發生! 哎呀! 解決方案是進行兩部分檢查。 一旦resolve
,我們已經討論過了。 第二次是在$stateChangeStart
事件中。 這里的關鍵是檢查並查看資源是否已解決。 如果是這樣,你在做同樣的安全檢查resolve
,但在事件。 如果資源沒有得到解決,那么check in resolve
將會resolve
它。 要實現此目的,您需要在服務中管理資源,以便適當地管理狀態。 其他一些misc。 筆記:
$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上。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.