youngwind / blog

梁少峰的个人博客
4.66k stars 385 forks source link

nodejs redis实战--如何写登录模块 #51

Open youngwind opened 8 years ago

youngwind commented 8 years ago

起因

redis是一个工具,用户管理保存在内存中的数据,常常被应用于缓存等。它有nodejs的使用版本,我想希望通过它来完成登录模块。

思路

大概思路如下:

  1. 用户登录成功之后,后端利用redis把用户token和id写进内存,并且回传token
  2. 前端接收到token之后把token写进cookie,这样以后每次请求都会带上cookie
  3. 在每个请求(除了登录/注册)之前都挂在token校验中间件,如果没有token或者token不正确,则抛出错误,否则进入下一个中间件。

    redis基本使用

    1. 安装

npm install redis --save

2. 最简单的例子

var redis = require("redis");
var client = redis.createClient();  // 创建redis实例

client.set("name", "youngwind", redis.print);
// 保存数据到内存

client.get("name", redis.print);
// 从内存中读取数据

3. 结果

2016-03-28 4 57 37

问题

1. 异步问题

nodejs-redis操作是异步的,而且不原生支持promise,要么写回调函数,要么包一层promise。官方readme里面推荐使用bluebird.

You can also use node_redis with promises by promisifying node_redis with bluebird.

我觉得有点麻烦,而且还有各种then,我希望直接使用co和yield来解决它,所以我找到了co-redis。 所以代码就变成了这样

var co = require('co');
var redisClient = require('redis').createClient();
var wrapper = require('co-redis');
var redisCo = wrapper(redisClient);
co(function* () {
  yield redisCo.set('test', 33);
  console.log(yield redisCo.get('test')); // logs 33 
});

2. 错误捕获

判断用户token是否有效的中间件,我一开始是这样写的。

module.exports = function () {
  return function (req, res, next) {
    var token = req.query.token;
    if (is.existy(token)) {
      co(function* () {
        var userId = yield redisCo.get(token);
        if (is.existy(userId)) {
          next();
        } else {
          throw new Error('token不正确');  // 这里不对
        }
      })
    } else {
      throw new Error('没有token');
    }
  }
};

但是有个问题,在抛出错误"token不正确"的时候并没有被错误中间件捕获。后来查了一下原因才发现,co本身会捕获错误,它把这个错误catch住了,所以express捕获不到。解决办法有两个。 1.把错误对象传给next()

if (is.existy(userId)) {
    next();
} else {
    next(new Error('token无效'));
}

2.利用co的catch

co(function* () {
        var userId = yield redisCo.get(token);
        if (is.existy(userId)) {
          next();
        } else {
           throw new Error('token不正确');
        }
      }).catch(function(err){
       throw new Error('token不正确');
})

不过这种写法重复抛出了错误,太挫,不使用。