NathanSuzuki
NathanSuzuki

Reputation: 157

redirecting a page with angular routing after successfully calling an api on express server

In a single page application using angular routing, how can I redirect a page after an api call. In my case, I want to redirect the user to the profile page after they have called the login api. So this is what I thought would work but it isn't.

On the client, main.js. I have the angular routing set up

app.config(function($routeProvider){   $routeProvider
//the home page display
.when('/', {
  templateUrl: 'main.html',
  controller: 'mainController'
})

.when('/login', {
  templateUrl: 'login.html',
  controller: 'loginController'
})

.when('/signUp', {
  templateUrl: 'signUp.html',
  controller: 'signUpController'
})

.when('/profile', {
  templateUrl: 'profile.html',
  //controller: 'mainController'
}); });

and from my controller I call the /login post api

app.controller('authController', function($scope, $http, $rootScope, $location){   
  $scope.user = {username: '', password: ''};   
  $scope.error_message = '';

  $scope.login = function(){
    $http.post('/login', $scope.user).success(function(data){
      if(data.state == 'success'){
        //set username authenticated property to true after successful log in
        //I am only pasting some of my code here, more logic before controller
        $rootScope.authenticated = true;
        $rootScope.current_user = "james";
        $location.path('/profile');
      }
      else{
        $scope.error_message = data.message;
      }
    });   
  }; 
});

and here is my login api

router.post('/login', passport.authenticate('local-login', {
        successRedirect : '/success', // redirect to the secure profile section
        failureRedirect : '/failure', // redirect back to the signup page if there is an error
        failureFlash : true // allow flash messages
    }));

and when it succeeds, it calls success which sends back the data which should trigger the callback in $http.post and redirect the page through $location.path('/profile');. However, the callback isn't called and my page displays the user information from res.send

//sends successful login state back to angular
    router.get('/success', function(req, res){
        res.send({state: 'success', user: req.user ? req.user : null});
    });

Am I on the right track? I am just following microsoft's tutorial https://www.microsoftvirtualacademy.com/en-us/training-courses/mean-stack-jump-start-8442 but their completed page on github doesn't even work so it doesn't help me debug this problem of mine.

Upvotes: 3

Views: 7130

Answers (3)

Nagaraj
Nagaraj

Reputation: 11

So far I haven't seen success in making Ajax calls to API redirecting to a page. We have a similar situation where API call may result in redirecting to a error page. We wanted to handle that in the server rather than asking UI (Angular) to do it. But it's just frustrating to see none of the methods of redirect like res.redirect are working. Our scenario is Angular makes a API call through Ajax and API running on Node.js should redirect to a html page.

Upvotes: 0

veggiesaurus
veggiesaurus

Reputation: 651

Using successRedirect and failureRedirect in passport will redirect the client to the specified pages, which will prevent your client-side angularJS routing from taking place. The reason you're seeing the user info after logging in is because your client is being redirected to the /success page, rather than actually responding to the original request. The client then fetches the success page with a GET request, and the new GET request is then responded to with the user info.

I would suggest leaving the node.js redirects out when using AngularJS, since you probably want to handle redirection on the client side:

router.post('/login', passport.authenticate('local-login'), function(req, res){
    res.send(req.user);
});

The inline function will never execute if the user is not authenticated. Instead, passport will respond directly with a 401 error status, with a body of "Unauthorized". Therefore the success state is not required. On the client side, you should use the .error() function to deal with 401 errors, rather than checking your state variable:

$http.post('/login', $scope.user).success(function(user){
    $rootScope.authenticated = true;
    $rootScope.current_user = "james";
    $location.path('/profile');
  })
 .error(function(err){
    $scope.error_message = err;
  });

If you want to pass back a more specific reason as to why the request was unauthorized (which is not always a good idea), you can use flash messages, and issue another GET request from angular to get a more detailed authorization failure message.

Upvotes: 3

John Munsch
John Munsch

Reputation: 19528

You seem to have a slight impedance mismatch on what the front-end and back-end want to do here. Your AngularJS code expects to make a POST to the API endpoint and get back a 200 (success) along with some JSON data which tells it about the success or failure of the login attempt.

The back-end, thinks it's going to receive a POST and then redirect the caller to a new location. At least that's the way I'm reading it. It's not simply sending back some data with an HTTP response code of 200 or an error code.

I think you want to tweak the back-end code to simply return data to the front-end to get the result you expect.

Upvotes: 0

Related Questions