AngularJS和ExpressJS会话管理?

时间:2013-08-28 16:47:47

标签: node.js angularjs express

我想在所有页面上保持会话。对于这个项目,我使用expressJs,nodeJS作为服务器端。 AngularJS在前端。

我不确定,如何在视图更改或网址更改时处理会话。因为我需要处理 expressJS路由器或angularJs路由器

我应该采用什么方法?

angularJS路由器

     myApp.config(['$routeProvider', function($routeProvider) {

    $routeProvider.when('/welcome', {templateUrl: 'partials/welcome.html', controller: 'MyCtrl2'});
    $routeProvider.when('/login', {templateUrl: 'partials/login.html', controller: 'MyCtrl2'});
    $routeProvider.when('/signup', {templateUrl: 'partials/signup.html', controller: 'singupController'});
    $routeProvider.otherwise({redirectTo: '/'});
  }]);

注册控制器

myApp.controller('singupController',function($scope,$rootScope,$http){

    $scope.doSingnup = function() {

       var formData = {
          'username' : this.username,
          'password' : this.password,
           'email' : null
      };

      var jdata = JSON.stringify(formData);

      $http({method:'POST',url:'/signup',data:jdata})
      .success(function(data,status,headers,config){

                console.log(data);

      }).
      error(function(data,status,headers,config){

        console.log(data)

      });
    }
  })

ExpressJS路由器

    module.exports = exports = function(app, db) {

    var sessionHandler = new SessionHandler(db);
    var contentHandler = new ContentHandler(db);

    // Middleware to see if a user is logged in
    app.use(sessionHandler.isLoggedInMiddleware);

    app.get('/', contentHandler.displayMainPage);

    app.post('/login', sessionHandler.handleLoginRequest);

    app.get('/logout', sessionHandler.displayLogoutPage);

    app.get("/welcome", sessionHandler.displayWelcomePage);

    app.post('/signup', sessionHandler.handleSignup);

    app.get('*', contentHandler.displayMainPage);

    // Error handling middleware
    app.use(ErrorHandler);
}

注册后,我想重定向到登录页面。我怎么能在上面的路由器中做到这一点。我应该使用以下哪一项来改变应用程序的视图           1)angularJS的$位置           2)ExpressJS的重定向

4 个答案:

答案 0 :(得分:8)

所以我遇到了同样的问题并且公平地说,我可能已经阅读了我不记得的地方。

问题:Angular构建单页应用。刷新后,您将放宽范围并使用经过身份验证的用户。

<强>方法

AngularJS modules提供一个名为 run 的启动函数,该函数在加载页面时始终调用。非常适合刷新/重新加载。

myApp.run(function ($rootScope, $location, myFactory) {
    $http.get('/confirm-login')
        .success(function (user) {
            if (user && user.userId) {
                $rootScope.user = user;
            }
        });
}

express-session 会为您保存会话,并使用您的浏览器发送的sessionId对您进行身份验证。因此,它始终知道您是否经过身份验证。

router.get('/confirm-login', function (req, res) {
        res.send(req.user)
    }
);

我所要做的就是,在刷新并加载所有依赖项之后,询问我是否经过身份验证并设置$ rootScope.user = authenticatedUserFromExpress;

答案 1 :(得分:7)

这里有两个不同的概念 - 服务器端会话状态和Angular中客户端的用户状态。在express中,您可以通过req.session使用会话来管理基于会话的数据。

在角度方面,控制器中只有范围。如果要跟踪多个控制器中的某些数据,则需要创建一个服务来存储数据并将服务注入所需的控制器。

典型的生命周期是首先检查服务中是否已存在数据,如果是,则使用它。如果没有,请等待数据填充(由用户或应用程序或其他),然后检测这些更改并与您的服务同步。

答案 2 :(得分:1)

注册控制器

function SignupCtrl($scope, $http, $location) {
    $scope.form = {}; // to capture data in form 
    $scope.errorMessage = ''; // to display error msg if have any
  $scope.submitPost = function() { // this is to submit your form can't do on
                                    //traditional way because it against angularjs SPA
    $http.post('/signup', $scope.form).
      success(function(data) {    // if success then redirect to "/" status code 200
        $location.path('/');
      }).error(function(err) {   // if error display error message  status code 400
                                 // the form can't be submitted until get the status code 200
        $scope.errorMessage = err;
      });
  };
}

sessionHandler.handleSignup

 this.handleSignup = function(req, res, next) {
        "use strict";
          // if you have a validate function pass the data from your
          // Signup controller to the function in my case is validateSignup
          // req.body is what you need 
        validateSignup(req.body, function(error, data) { 
        if(error) {
            res.send(400, error.message); // if error send error message to angularjs
        }else {
            // do something else
            // rmb to res.send(200) 

        }
    });

}

validatesignup

function validateSignup(data,callback) {
        "use strict";   // the data is req.body 
           //so now you can access your data on your form 
           // e.g you have 2 fields name="password" and name="confirmPassword on your form"
          var pass = data.password,
              comPass = data.confirmPassword;
           if(pass != comPass){
               callback(new Error('Password must match'), null); 
                  // then show the error msg on the form by using 
                  //angular ng-if like <div ng-if="errorMessage">{{errorMessage}}</div>
           }else{
               callback(null, data);
            }



  }

希望这个帮助

答案 3 :(得分:0)

在这里的所有答案中,我最喜欢@ alknows的方法。但是,与其他建议您向服务器发送请求以获取当前用户数据的答案一样,我会对它们提出几个问题:

  • 您必须通过AJAX($ http)电话处理竞争条件。
  • 您已经在呈现index.html
  • 后向服务器发送了不必要的请求

我尝试了@ alknow的方法,在我能够解决由于我的角度应用程序控制器和配置需要当前用户完成工作后出现的许多竞争条件后,它为我解决了问题。我尽量在适当的时候避免竞争条件,所以我有点不愿意继续这种方法。所以我想到了一个更好的方法:使用index.html发送当前用户数据并将其存储在本地。

我的方法:在index.html&amp;中嵌入currentUser在客户端本地存储

在服务器上的index.html中,创建一个脚本标记来保存您想要传递给客户端的任何数据:

```

<!--YOUR OTHER index.html stuff go above here-->
<script id="server-side-rendered-client-data" type="text/javascript">
    var __ssr__CData = {
        currentUser: { id: '12345', username: 'coolguy', etc: 'etc.' }
    }
</script>

```

然后,正如@alknows建议的那样,在app.js或您启动角度应用的任何地方,添加app.run(..., () => {...})。在app.run()中,您将需要获取服务器端呈现的客户端数据对象,我将其命名为__ssr_CData,以便我不太可能在以后的其他javascript中遇到跨全局命名空间的名称冲突:

var myAngularApp = angular.module("mainApp", ['ngRoute']);

myAngularApp.run(function ($rootScope) {
    const currentUserFromServer = __ssr__CData.currentUser
    const currentUserAccessTokenFromServer = __ssr__CData.accessToken
    const currentUser = 
    CurrentUser.set(currentUserAccessTokenFromServer, currentUserFromServer)
    $rootScope.currentUser = currentUser
});

如您所知,只要页面完全重新加载,就会调用app.run()CurrentUser是一个全局类,用于在单页面环境中管理我的角度应用程序的当前用户。因此,当我调用CurrentUser.set(...)时,它会将当前用户数据存储在我稍后可以通过调用CurrentUser.get()在我的角度应用中检索的位置。因此,在您的任何角度应用程序控制器中,您现在只需执行以下操作即可检索服务器提供的当前用户:

myAngularApp.controller('loginController',function($scope, $rootScope, $http){
   //check if the user is already logged in:
   var currentUser = CurrentUser.get()
   if(currentUser) {
       alert("HEY! You're already logged in as " +currentUser.username)
      return $window.location.href = "/";
   }

   //there is no current user, so let user log in
   //...
}

在该示例中,我使用了上面解释过的CurrentUser.get()来从服务器获取先前存储的当前用户。我也可以通过访问$rootScope.currentUser来检索当前用户,因为我也将其存储在那里。它取决于你。

myAngularApp.controller('signupController',function($scope, $rootScope, $http){
   //check if the user is already logged in:
   var currentUser = CurrentUser.get()
   if(currentUser) {
       alert("HEY! You're already logged in as " +currentUser.username)
      return $window.location.href = "/";
   }

   //there is no current user, so let user signup
   //... you run your signup code after getting form data
   $http({method:'POST',url:'/signup',data:jdata})
       .success(function(data,status,headers,config){
           //signup succeeded!
           //set the current user locally just like in app.js
           CurrentUser.set(data.newUser)
           //send user to profile
           return $window.location.href = "/profile";
       })
       .error(function(data,status,headers,config){
           //something went wrong
           console.log(data)
      });

}

现在,在新用户注册后,您的服务器从AJAX调用返回了新用户。我们通过调用CurrentUser.set(...)将该新用户设置为当前用户,并将用户发送到他们的个人资料。您现在可以像检查当前用户是否存在于登录和注册控制器中一样,在配置文件控制器中获取当前用户。

我希望这可以帮助遇到此问题的任何人。为了供您参考,我使用client-sessions module来处理我服务器上的会话。