$state.go 没有按照承诺工作
$state.go not working from promise
我正在尝试实施基本的安全检查,以阻止用户根据他们的权限集访问某些状态:
'use strict';
var autoExecApp = angular.module("myApp", ['ui.router']);
autoExecApp.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider){
$stateProvider
.state('index', {
url: '/index',
templateUrl: 'partials/index.html'
})
.state('permission', {
url: '/permission',
templateUrl: 'partials/permissions.html'
});
}]);
autoExecApp.run(['$rootScope', '$state', '$userRoles', function($rootScope, $state, $userRoles) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
event.preventDefault(); // prevent page from being loaded anyway
$userRoles.hasPermissions(toState.name).then(function(data) {
var result = data === "true";
if (!result) {
$state.go('permission');
}
else {
$state.go(toState.name, toParams ,{notify:false});
}
});
});
}]);
现在问题围绕着我的状态更改发生在承诺内这一事实。我很乐意让它同步——在这种情况下它确实应该是同步的,但我很困惑为什么它不异步工作。
如果我将 event.preventDefault()
移动到 promise 中,它就会起作用。但是状态分两步改变。首先,它将始终转到原始页面,然后在转换到 'blocked' 页面后不久(如果它被阻止)。我认为这是因为它 returns 从 $on 成功一次并更新状态,然后当它 returns 从承诺中再次更新它。
如果承诺如您所见,状态将不会改变。一切似乎都在工作,但页面不会更新视图。我不确定为什么会这样。出于某种原因,如果事件没有被阻止,后面的状态改变将起作用。但是,如果它被阻止并且 $state.go 从返回的承诺中调用,则更新 $state 似乎为时已晚。
编辑:我成功使用了广播解决方案,这就是它的样子:
event.preventDefault();
$state.go(toState.name, toParams, {notify: false}).then(function() {
// line 907 state.js
$rootScope.$broadcast('$stateChangeSuccess', toState, toParams, fromState, fromParams);
});
显然是因为它在 promise 中,preventDefault();抑制此“$stateChangeSuccess”自然触发。手动执行它会恢复预期的行为。
这是一个已知问题。
通常有效的解决方法是从“$stateChangeStart”处理程序中广播自定义事件,然后从该自定义事件的处理程序中进行状态转换。
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
event.preventDefault(); // prevent page from being loaded anyway
$userRoles.hasPermissions(toState.name).then(function(data) {
var result = data === "true";
if (!result) {
$rootScope.$broadcast('goToPermission');
}
else {
$rootScope.$broadcast('goToState', toState.name);
}
});
});
借自:
https://github.com/angular-ui/ui-router/issues/178#issuecomment-49156829
为什么不使用解析函数来检查用户是否可以访问您的路由?
像这样:
autoExecApp.config(['$stateProvider', '$urlRouterProvider', function (
$stateProvider, $urlRouterProvider) {
$stateProvider
.state('index', {
url: '/index',
templateUrl: 'partials/index.html'
})
.state('permission', {
url: '/permission',
templateUrl: 'partials/permissions.html'
})
.state('restricted-route', {
url: '/restricted',
templateUrl: 'partials/restricted.html',
resolve: {
authenticated: function ($q) {
var deferred = $q.defer();
$userRoles.hasPermissions(this)
.then(function (data) {
var result = data === "true";
if (result) {
deferred.resolve();
} else {
deferred.reject('unauthorized');
}
});
return deferred.promise;
}
}
});
}]);
$rootScope.$on('$stateChangeError',
function (event, toState, toParams, fromState, fromParams, rejection) {
if (rejection.error === 'unauthorized') {
$state.go('permission');
}
}
}
我认为 Vladimir Gurovich 的 "broadcast" 解决方案是可行的方法,尤其是当他指出这是一个既定的解决方法时。
但我想知道缓存权限然后同步查找的解决方案是否也可行。
autoExecApp.run(['$rootScope', '$state', '$userRoles', function($rootScope, $state, $userRoles) {
// Create a permissions cache, seeded with
// permission to go to the 'permission' state.
var permissionCache = {
'permission': true
};
// Create a researched list of all possible state names.
var stateNames = ['stateA', 'stateB', 'stateC'];
// Now load the cache asynchronously with
// `true` for each permission granted.
stateNames.forEach(function(state) {
$userRoles.hasPermissions(state).then(function(data) {
if(data === "true") {
permissionCache[state] = true;
}
});
});
// And finally, establish a $stateChangeStart handler
// that looks up permissions **synchronously**,
// in the permissionCache.
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
if (!permissionCache[toState.name]) {
event.preventDefault(); // prevent page from being loaded
$state.go('permission');
}
});
}]);
当然,这个解决方案有其自身的问题:
- 依赖于对所有可能的州名的深入研究。
- 在加载缓存之前尝试的任何状态更改都将被拒绝。
- 页面生命周期中 userRoles 的任何更改都需要清除缓存并重新加载。
没有 research/testing,我不能说这些问题是否重要。
我正在尝试实施基本的安全检查,以阻止用户根据他们的权限集访问某些状态:
'use strict';
var autoExecApp = angular.module("myApp", ['ui.router']);
autoExecApp.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider){
$stateProvider
.state('index', {
url: '/index',
templateUrl: 'partials/index.html'
})
.state('permission', {
url: '/permission',
templateUrl: 'partials/permissions.html'
});
}]);
autoExecApp.run(['$rootScope', '$state', '$userRoles', function($rootScope, $state, $userRoles) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
event.preventDefault(); // prevent page from being loaded anyway
$userRoles.hasPermissions(toState.name).then(function(data) {
var result = data === "true";
if (!result) {
$state.go('permission');
}
else {
$state.go(toState.name, toParams ,{notify:false});
}
});
});
}]);
现在问题围绕着我的状态更改发生在承诺内这一事实。我很乐意让它同步——在这种情况下它确实应该是同步的,但我很困惑为什么它不异步工作。
如果我将 event.preventDefault()
移动到 promise 中,它就会起作用。但是状态分两步改变。首先,它将始终转到原始页面,然后在转换到 'blocked' 页面后不久(如果它被阻止)。我认为这是因为它 returns 从 $on 成功一次并更新状态,然后当它 returns 从承诺中再次更新它。
如果承诺如您所见,状态将不会改变。一切似乎都在工作,但页面不会更新视图。我不确定为什么会这样。出于某种原因,如果事件没有被阻止,后面的状态改变将起作用。但是,如果它被阻止并且 $state.go 从返回的承诺中调用,则更新 $state 似乎为时已晚。
编辑:我成功使用了广播解决方案,这就是它的样子:
event.preventDefault();
$state.go(toState.name, toParams, {notify: false}).then(function() {
// line 907 state.js
$rootScope.$broadcast('$stateChangeSuccess', toState, toParams, fromState, fromParams);
});
显然是因为它在 promise 中,preventDefault();抑制此“$stateChangeSuccess”自然触发。手动执行它会恢复预期的行为。
这是一个已知问题。 通常有效的解决方法是从“$stateChangeStart”处理程序中广播自定义事件,然后从该自定义事件的处理程序中进行状态转换。
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
event.preventDefault(); // prevent page from being loaded anyway
$userRoles.hasPermissions(toState.name).then(function(data) {
var result = data === "true";
if (!result) {
$rootScope.$broadcast('goToPermission');
}
else {
$rootScope.$broadcast('goToState', toState.name);
}
});
});
借自: https://github.com/angular-ui/ui-router/issues/178#issuecomment-49156829
为什么不使用解析函数来检查用户是否可以访问您的路由?
像这样:
autoExecApp.config(['$stateProvider', '$urlRouterProvider', function (
$stateProvider, $urlRouterProvider) {
$stateProvider
.state('index', {
url: '/index',
templateUrl: 'partials/index.html'
})
.state('permission', {
url: '/permission',
templateUrl: 'partials/permissions.html'
})
.state('restricted-route', {
url: '/restricted',
templateUrl: 'partials/restricted.html',
resolve: {
authenticated: function ($q) {
var deferred = $q.defer();
$userRoles.hasPermissions(this)
.then(function (data) {
var result = data === "true";
if (result) {
deferred.resolve();
} else {
deferred.reject('unauthorized');
}
});
return deferred.promise;
}
}
});
}]);
$rootScope.$on('$stateChangeError',
function (event, toState, toParams, fromState, fromParams, rejection) {
if (rejection.error === 'unauthorized') {
$state.go('permission');
}
}
}
我认为 Vladimir Gurovich 的 "broadcast" 解决方案是可行的方法,尤其是当他指出这是一个既定的解决方法时。
但我想知道缓存权限然后同步查找的解决方案是否也可行。
autoExecApp.run(['$rootScope', '$state', '$userRoles', function($rootScope, $state, $userRoles) {
// Create a permissions cache, seeded with
// permission to go to the 'permission' state.
var permissionCache = {
'permission': true
};
// Create a researched list of all possible state names.
var stateNames = ['stateA', 'stateB', 'stateC'];
// Now load the cache asynchronously with
// `true` for each permission granted.
stateNames.forEach(function(state) {
$userRoles.hasPermissions(state).then(function(data) {
if(data === "true") {
permissionCache[state] = true;
}
});
});
// And finally, establish a $stateChangeStart handler
// that looks up permissions **synchronously**,
// in the permissionCache.
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
if (!permissionCache[toState.name]) {
event.preventDefault(); // prevent page from being loaded
$state.go('permission');
}
});
}]);
当然,这个解决方案有其自身的问题:
- 依赖于对所有可能的州名的深入研究。
- 在加载缓存之前尝试的任何状态更改都将被拒绝。
- 页面生命周期中 userRoles 的任何更改都需要清除缓存并重新加载。
没有 research/testing,我不能说这些问题是否重要。