最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - AngularJS : angular-ui-router always redirects to $urlRouterProvider.otherwise location - Stack Overflow

programmeradmin3浏览0评论

I'm trying to create an SPA where you have to be logged in to access almost everything. So naturally, the default screen you see is the login screen. However, after a user has logged in, no matter what the ui-sref is, ui-router redirects to the login page (even when the user is authenticated). Here is my ui-router code:

(function () {
'use strict';
angular
    .module('app', ['ui.router', 'satellizer'])
    .config(function ($stateProvider, $urlRouterProvider, $authProvider, $httpProvider, $provide) {
  
        $httpProvider.interceptors.push(['$q', '$injector', function($q, $injector){
            return {
                responseError: function (rejection) {
                    var $state = $injector.get('$state');
                    var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
                    angular.forEach(rejectionReasons, function (value, key) {
                        if (rejection.data.error === value) {
                            localStorage.removeItem('user');
                            $state.go('auth');
                        }
                    });

                    return $q.reject(rejection);
                },
                response: function(response) {
                    var authorization = response.headers('authorization');
                    if(authorization !== null) {
                        authorization = authorization.substr(7).trim();
                        //console.log(authorization);
                        var $auth = $injector.get('$auth');
                        $auth.setToken(authorization);
                    }
                    return response;
                }
            }
        }]);

        $authProvider.loginUrl = 'mingdaograder/api/authenticate';

        $stateProvider
            .state('users', {
                url: '/users',
                templateUrl: 'views/userView.html',
                controller: 'UserController as user'
            })
            .state('subjects', {
                url: '/users/:user_id/subjects',
                templateUrl: 'views/subjectsView.html',
                controller: 'SubjectsCtrl as subjectsCtrl'
            })
            .state('subject', {
                url: '/users/:user_id/subjects/:subject_id',
                templateUrl: 'views/subjectView.html',
                controller: 'SubjectCtrl as subjectCtrl'
            })
            .state('auth', {
                url: '/auth',
                templateUrl: 'views/authView.html',
                controller: 'AuthController as auth'
            });
            //.state('otherwise', {
            //    url: '*path',
            //    templateUrl: 'views/authView.html',
            //    controller: 'AuthController as auth'
            //});

            //$urlRouterProvider.otherwise('/auth');
            $urlRouterProvider.otherwise(function($injector, $location) {
                console.log("Could not find " + $location);
                $location.path('/auth');
            });
    })
    .run(function ($rootScope, $state, $log) {
        $rootScope.$on('$stateChangeStart', function (event, toState) {
                console.log(toState.name);
            var user = JSON.parse(localStorage.getItem('user'));
            if (user) {
                $rootScope.authenticated = true;
                $rootScope.currentUser = user;
            }
        }
        );
    }
);
})();

I'm trying to create an SPA where you have to be logged in to access almost everything. So naturally, the default screen you see is the login screen. However, after a user has logged in, no matter what the ui-sref is, ui-router redirects to the login page (even when the user is authenticated). Here is my ui-router code:

(function () {
'use strict';
angular
    .module('app', ['ui.router', 'satellizer'])
    .config(function ($stateProvider, $urlRouterProvider, $authProvider, $httpProvider, $provide) {
  
        $httpProvider.interceptors.push(['$q', '$injector', function($q, $injector){
            return {
                responseError: function (rejection) {
                    var $state = $injector.get('$state');
                    var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
                    angular.forEach(rejectionReasons, function (value, key) {
                        if (rejection.data.error === value) {
                            localStorage.removeItem('user');
                            $state.go('auth');
                        }
                    });

                    return $q.reject(rejection);
                },
                response: function(response) {
                    var authorization = response.headers('authorization');
                    if(authorization !== null) {
                        authorization = authorization.substr(7).trim();
                        //console.log(authorization);
                        var $auth = $injector.get('$auth');
                        $auth.setToken(authorization);
                    }
                    return response;
                }
            }
        }]);

        $authProvider.loginUrl = 'mingdaograder/api/authenticate';

        $stateProvider
            .state('users', {
                url: '/users',
                templateUrl: 'views/userView.html',
                controller: 'UserController as user'
            })
            .state('subjects', {
                url: '/users/:user_id/subjects',
                templateUrl: 'views/subjectsView.html',
                controller: 'SubjectsCtrl as subjectsCtrl'
            })
            .state('subject', {
                url: '/users/:user_id/subjects/:subject_id',
                templateUrl: 'views/subjectView.html',
                controller: 'SubjectCtrl as subjectCtrl'
            })
            .state('auth', {
                url: '/auth',
                templateUrl: 'views/authView.html',
                controller: 'AuthController as auth'
            });
            //.state('otherwise', {
            //    url: '*path',
            //    templateUrl: 'views/authView.html',
            //    controller: 'AuthController as auth'
            //});

            //$urlRouterProvider.otherwise('/auth');
            $urlRouterProvider.otherwise(function($injector, $location) {
                console.log("Could not find " + $location);
                $location.path('/auth');
            });
    })
    .run(function ($rootScope, $state, $log) {
        $rootScope.$on('$stateChangeStart', function (event, toState) {
                console.log(toState.name);
            var user = JSON.parse(localStorage.getItem('user'));
            if (user) {
                $rootScope.authenticated = true;
                $rootScope.currentUser = user;
            }
        }
        );
    }
);
})();

Anytime I try to use $state.go(any state name here) or even type the address into the address bar, I am always redirected to the auth state. On the console the message is "Could not find http://localhost/#/" for every single route. I can type in http://localhost/#/users/5/subjects and I get the same message.

Here is one of my controllers doing a redirect:

(function () {
    'use strict';

    angular
        .module('app')
        .controller('AuthController', AuthController);

    function AuthController($auth, $state, $http, $rootScope, $log) {
        var vm = this;

        vm.loginError = false;
        vm.loginErrorText;

        vm.login = function () {
            var credentials = {
                username: vm.username,
                password: vm.password
            };

            $auth.login(credentials).then(function () {
                return $http.get('api/authenticate/user');
            }, function (error) {
                vm.loginError = true;
                vm.loginErrorText = error.data.error;
            }).then(function (response) {
                var user = JSON.stringify(response.data.user);
                localStorage.setItem('user', user);
                $rootScope.authenticated = true;
                $rootScope.currentUser = response.data.user;

                //$log.info('From AuthCtrl: ' + $rootScope.currentUser.id);
                $state.go('subjects', {user_id:$rootScope.currentUser.id});
            });
        }
    }
})();

Any ideas what I'm doing wrong? Thanks a lot for your time.

Update: Ok, I haven't found a way to fix it but I think I may have found a possible cause. It seems to only happen for the routes with parameters. For example, if I go to the users state, whose path is /users, there is no redirect. However, if I go to the subjects state, whose path is /users/:user_id/subjects, it does redirect. It's like the Url matching service can't recognize that /users/5/subjects matches /users/:user_id/subjects, so redirects. Any ideas how to work around this?

Share Improve this question edited Sep 29, 2015 at 12:38 John Slegers 47.1k23 gold badges204 silver badges173 bronze badges asked Aug 8, 2015 at 16:32 Brent ParkerBrent Parker 7391 gold badge5 silver badges20 bronze badges 7
  • otherwise needs to be the last statement in the chain, since the first acceptable match will be selected. – Claies Commented Aug 8, 2015 at 16:38
  • did you use ui-view and where is abstract state ? – gaurav bhavsar Commented Aug 8, 2015 at 16:42
  • @guarav bhavsar Yes, I use ui-view in the HTML. Do I need an abstract state? I'm pretty new to angular and angular-ui-router. – Brent Parker Commented Aug 8, 2015 at 16:58
  • @BrentParker a lot of approaches for authorization use a parent state (like "auth") to enforce the login requirement. All other states which require login, should be a child of this state (eg. "auth.users" and "auth.subjects"). Typically, you might also make the parent state be an abstract state, b/c it's not a state that you can actually visit (this decision is up to you/your app). – Sunil D. Commented Aug 8, 2015 at 17:01
  • @Sunil D. I'm enforcing the authorization via the server using JWT. What I mean is, any information that is retrieved from the server is retrieved via $http.get('myserver/api/some/resource') with an Authorization header. If you don't pass the correct JWT, an error is returned from the server and no sensitive data is sent. In javascript, I've added an interceptor to $httpProviders that checks the response. If there's an error, you go back to the login page (auth state). Why use /auth/users instead of just /users when you have to be logged in for everything? – Brent Parker Commented Aug 8, 2015 at 17:09
 |  Show 2 more ments

4 Answers 4

Reset to default 9

I found I didn't have a '/' at the beginning of my initial state url. Every time I navigated to the state, the missing '/' seemed to push it into the stateProvider.otherwise.

state1: 'opportunity'
state1Url : '/opportunity/' <== added initial forward slash to make it work.

state2: 'opportunity.create'
state2Url : 'create/'

The first path to be recognised will be the selected as the current location. This means that the order of your route definitions is crucially important. In your case you only have a single catch-all otherwise route definition and since all routes match this then all routes are directed to your login page ignoring any other route definitions you may have, including all your stateProvider state definitions.

One way to fix this is to remove the urlRouterProvider route definition altogether and instead use the *path syntax provided by ui-router to create an alternative otherwise state (which must be defined last for the same reasons given above).

Therefore your code might look something like this:

$stateProvider
    .state('auth', {
        url: '/auth',
        templateUrl: 'views/authView.html',
        controller: 'AuthController as auth'
    })
    .state('users', {
        url: '/users',
        templateUrl: 'views/userView.html',
        controller: 'UserController as user'
    })
    .state('subjects', {
        url: '/users/:user_id/subjects',
        templateUrl: 'views/subjectsView.html',
        controller: 'SubjectsCtrl as subjectsCtrl'
    })
    .state('subject', {
        url: '/users/:user_id/subjects/:subject_id',
        templateUrl: 'views/subjectView.html',
        controller: 'SubjectCtrl as subjectCtrl'
    })
    .state("otherwise", {
        url: "*path",
        templateUrl: 'views/authView.html',
        controller: 'AuthController as auth'
    });

From experience, this is either due to the / missing at either the beginning or the end of the url route property definition.

Make sure for parent routes to add the initial forward slash to your routes.

 .state('checkers', {
                url: '/checkers/',
                templateUrl: 'checkers.html',
                controller: 'CheckersController',
                title: 'Checker',
            })

(function () {
'use strict';
angular
    .module('app', ['ui.router', 'satellizer'])
    .config(function ($stateProvider, $urlRouterProvider, $authProvider, $httpProvider, $provide) {
  
        $httpProvider.interceptors.push(['$q', '$injector', function($q, $injector){
            return {
                responseError: function (rejection) {
                    var $state = $injector.get('$state');
                    var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
                    angular.forEach(rejectionReasons, function (value, key) {
                        if (rejection.data.error === value) {
                            localStorage.removeItem('user');
                            $state.go('auth');
                        }
                    });

                    return $q.reject(rejection);
                },
                response: function(response) {
                    var authorization = response.headers('authorization');
                    if(authorization !== null) {
                        authorization = authorization.substr(7).trim();
                        //console.log(authorization);
                        var $auth = $injector.get('$auth');
                        $auth.setToken(authorization);
                    }
                    return response;
                }
            }
        }]);

        $authProvider.loginUrl = 'mingdaograder/api/authenticate';

        $stateProvider
            .state('users', {
                url: '/users',
                templateUrl: 'views/userView.html',
                controller: 'UserController as user'
            })
            .state('subjects', {
                url: '/users/:user_id/subjects',
                templateUrl: 'views/subjectsView.html',
                controller: 'SubjectsCtrl as subjectsCtrl'
            })
            .state('subject', {
                url: '/users/:user_id/subjects/:subject_id',
                templateUrl: 'views/subjectView.html',
                controller: 'SubjectCtrl as subjectCtrl'
            })
            .state('auth', {
                url: '/auth',
                templateUrl: 'views/authView.html',
                controller: 'AuthController as auth'
            });
            //.state('otherwise', {
            //    url: '*path',
            //    templateUrl: 'views/authView.html',
            //    controller: 'AuthController as auth'
            //});

            //$urlRouterProvider.otherwise('/auth');
            $urlRouterProvider.otherwise(function($injector, $location) {
                console.log("Could not find " + $location);
                $location.path('/auth');
            });
    })
    .run(function ($rootScope, $state, $log) {
        $rootScope.$on('$stateChangeStart', function (event, toState) {
                console.log(toState.name);
            var user = JSON.parse(localStorage.getItem('user'));
            if (user) {
                $rootScope.authenticated = true;
                $rootScope.currentUser = user;
            }
        }
        );
    }
);
})();

发布评论

评论列表(0)

  1. 暂无评论