7

我想在所有页面上保持会话。对于这个项目,我使用 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 的 $location
2) ExpressJS 的重定向

4

4 回答 4

8

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

问题:Angular 构建单页应用程序。刷新后,您失去了范围,并随之失去了经过身份验证的用户。

方法

AngularJS 模块提供了一个名为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;

于 2015-04-23T12:31:46.977 回答
7

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

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

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

于 2013-08-28T16:54:34.667 回答
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) 

        }
    });

}

验证注册

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



  }

希望这有帮助

于 2013-12-08T08:09:21.613 回答
0

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

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

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

我的方法:在 index.html 中嵌入 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您启动 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 应用程序控制器中,您现在可以通过简单地执行以下操作来检索服务器提供的当前用户:

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 模块来处理我的服务器上的会话。

于 2017-07-08T20:50:45.140 回答