Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle redirects in angularjs $http

I'm using angularjs http service in my app, and I noticed this in the website:

If the AJAX call succeeds (the server sends back an HTTP code between 200 and 209), the function passed to the success() function is executed. If the AJAX call fails (all other codes except for redirects), the function passed to the error() method is executed.

Obviously, the redirects status code isn't part of success neither error callbacks. So how are we going to handle redirects?

This is what my script does, get data from server using http get. If session expires, server returns 302 status code. I should catch that status code then redirect the page to login.

app.controller('GraphController', function($scope, $http, localStorageService, $interval) {
        $scope.GraphData = [{"key":"in","values":[]},{"key":"out","values":[]}]; 

        $scope.pollStats = function() {
            $http.get('/statistics/getStat').success(function(lastData, status, headers) {              
                if (status==302) {
                    //this doesn't work
                    //window.location=headers('Location');
                    //this doesn't work either
                    window.location.replace(headers('Location'));
                }else if (status==200) {
                    ...processData
                }
            });
        };
});

Obviously my script won't work because success callback can't handle redirects. So how are we supposed to handle that?

like image 853
user3714598 Avatar asked Apr 06 '15 10:04

user3714598


3 Answers

Please note that this is specific to my project.

Goal: Catch 302 status code and redirect the page (to login in my case).

Result: In firebug, I could see that the response code is 302 but then I found out that angularjs returns 200 by simply printing the value of status (response.status). So at first you'd thought that you're hopeless. But in my case, what I did was get the data (response.data), look for the string that could be only found in my login page. Then that's it. problem solved :D

The idea was taken here.

Here's the code

app.factory('redirectInterceptor', function($q,$location,$window){
    return  {
        'response':function(response){
        if (typeof response.data === 'string' && response.data.indexOf("My Login Page")>-1) {
            console.log("LOGIN!!");
            console.log(response.data);
            $window.location.href = "/login.html";
            return $q.reject(response);
        }else{
            return response;
        }
        }
    }

    });

    app.config(['$httpProvider',function($httpProvider) {
        $httpProvider.interceptors.push('redirectInterceptor');
    }]); 
like image 103
user3714598 Avatar answered Nov 04 '22 15:11

user3714598


I ran into this problem as well, trying to find a nice way of implementing that a user be prompted to change his password, when logging in for the first time.

The way I solved this problem is by letting the server return a (unofficial) 210 status code with a location header, containing the information needed for the UI-Router's state provider. In the controller at the front I have added:

if (data.status == 210) {
  var location = data.headers().location;
  $state.go(location);
} else {
  $state.go("user", {name: data.data.fullName, userId: data.data.userId});
}
like image 2
Albert Waninge Avatar answered Nov 04 '22 15:11

Albert Waninge


I think there is a better way than the http interceptor for login redirection because it parses the request response and if another page has the string requested, an error will occur.

I suggest wrapping $http methods in your service and check if the user is logged before making the request:

app.service('httpWrapper', ['$http','requestLogin',function($http,requestLogin){
  var httpWrapper = {};

  // You can add methods for put, post
  httpWrapper.get = function(url){
    // Before making the request, check with a request if the user is logged
    $http.get(urlLogged).success(function (auth) {
        if(!auth.logged){
            // Show login widget
            requestLogin.show();
        }
    }).error(function(){

    });        
    return $http.get(url);
  }

  return httpWrapper;
}]);

Then uses httpWrapper in place of $http in your other services:

app.service('otherService', ['httpWrapper',function(httpWrapper){
  httpWrapper.get(url);
}]);

Here the service for requestLogin:

app.service('requestLogin', ['$rootScope',function($rootScope){
  var requestLogin = {};

  $rootScope.showlogin = false;

  requestLogin.show = function(){
    $rootScope.showlogin = true;
  }

  requestLogin.hide = function(){
    $rootScope.showlogin = false;
  }    
  return requestLogin;
}]);

And then, in your view:

<div ng-show="showlogin == true">
  Please log-in...
</div>
like image 1
Gautier Drusch Avatar answered Nov 04 '22 14:11

Gautier Drusch