AngularJS和ExpressJS会话管理



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

我不确定,当视图更改或url更改时,如何处理会话。因为我需要同时处理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 的重定向

所以我遇到了同样的问题,公平地说,我可能在我不记得的地方读到了这个方法。

问题:Angular构建单页应用程序。刷新后,您会释放作用域,并随之释放经过身份验证的用户。

方法

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

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

快速会话为您保存会话,并使用浏览器发送的会话ID对您进行身份验证。所以它总是知道你是否通过了身份验证。

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

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

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

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

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

注册控制器

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) 
        }
    });
}

验证设计

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);
            }

  }

希望这能帮助

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

  • 由于AJAX($http)调用,您必须处理竞争条件
  • 在服务器已经呈现了您的index.html之后,您向服务器发送了一个不必要的请求

我尝试了@alknow的方法,在我解决了由于我的角度应用程序控制器和配置需要当前用户来完成他们的工作而出现的许多比赛条件后,它对我来说是可行的。我尽量在适当的时候避开比赛条件,所以我有点不愿意继续这种方法。所以我想到了一个更好的方法:用index.html发送当前用户数据并将其存储在本地。

我的方法:在index.html&在客户端本地存储

在服务器上的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中或在您启动angular应用程序的任何位置,添加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是一个全局类,用于在单页环境中管理我的angular应用程序的当前用户。因此,当我调用CurrentUser.set(...)时,它会将当前用户数据存储在一个地方,我稍后可以通过调用CurrentUser.get()在我的angular应用程序中检索。因此,在任何一个angular应用程序控制器中,您现在都可以通过简单的操作检索当前用户提供的服务器:

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(...)将该新用户设置为当前用户,并将该用户发送到他们的配置文件中。现在,您可以在配置文件控制器中获取当前用户,方法与检查登录和注册控制器中是否存在当前用户相同。

我希望这能帮助到任何遇到这种情况的人。供您参考,我正在使用客户端会话模块来处理服务器上的会话。

最新更新