最近在研究 express,学着使用 cookie,开始不会用,就百度了一下,没有百度到特别完整的解答。查阅了 express 的 API,综合了网友的博客,解读了 cookie-parser 的源码,以及使用 WebStorm 和 Chrome 验证,终于明白了 express 中 cookie 的使用。顾此篇文章即是分享也是总结。
# 1. cookie 的创建
express 直接提供了 api, 只需要在需要使用的地方调用如下 api 即可
| function(req, res, next){ | |
|         ... | |
| res.cookie(name, value [, options]); | |
|         ... | |
|     } | 
express 就会将其填入 Response Header 中的 Set-Cookie,达到在浏览器中设置 cookie 的作用。
- name: 类型为 String
- value: 类型为 String 和 Object,如果是 Object 会在 cookie.serialize () 之前自动调用 JSON.stringify 对其进行处理
- Option: 类型为对象,可使用的属性如下
   domain:cookie在什么域名下有效,类型为String,。默认为网站域名
   expires: cookie过期时间,类型为Date。如果没有设置或者设置为0,那么该cookie只在这个这个session有效,即关闭浏览器后,这个cookie会被浏览器删除。
   httpOnly: 只能被web server访问,类型Boolean。
   maxAge: 实现expires的功能,设置cookie过期的时间,类型为String,指明从现在开始,多少毫秒以后,cookie到期。
   path: cookie在什么路径下有效,默认为'/',类型为String
   secure:只能被HTTPS使用,类型Boolean,默认为false
   signed:使用签名,类型Boolean,默认为false。`express会使用req.secret来完成签名,需要cookie-parser配合使用`
上面是我结合实验和自己的理解,对官网 api 的翻译。原英文如下:
用例如下
| res.cookie('name', 'koby', { domain: '.example.com', path: '/admin', secure: true }); | |
| //cookie 的有效期为 900000ms | |
| res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }); | |
| //cookie 的有效期为 900000ms | |
| res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }); | |
| //cookie 的 value 为对象 | |
| res.cookie('cart', { items: [1,2,3] }); | |
| res.cookie('cart', { items: [1,2,3] }, { maxAge: 900000 }); | |
| res.cookie('name', 'tobi', { signed: true }); | 
# 2.cookie 的删除
express 直接提供了 api 删除浏览器中的 cookie, 只需要在需要使用的地方调用如下 api 即可
| function(req, res, next){ | |
|         ... | |
| res.clearCookie(name [, options]); | |
|         ... | |
|     } | 
# 3. 利用 cookie-parser 读取 cookie
cookie-parser 是一个非常好用方便的插件,可以直接用在 express 和 connect 中,官文地址为 https://www.npmjs.com/package/cookie-parser。npm 安装命令
| $npm install cookie-parser --save | 
使用方式
| var express = require('express'); | |
| var cookieParser = require('cookie-parser'); | |
| var app = express(); | |
| // 不使用签名 | |
| app.use(cookiePareser()); | |
| // 若需要使用签名,需要指定一个 secret, 字符串,否者会报错 | |
| app.use(cookiePareser('Simon')); | 
・如果没有为 signed 功能,cookie-parser 通过如下代码解析 req.headers.cookie
| //index.js | |
| var cookie = require('cookie'); | |
| var parse = require('./lib/parse'); | |
| if (req.cookies) return next(); // 如果存在 req.cookies 跳过这个 middleware | |
| var cookies = req.headers.cookie; // 保存对象地址,提高运行效率 | |
| req.cookies = Object.create(null); // 创建一个对象,解析后的且未使用签名的 cookie 保存在 req.cookies 中 | |
| req.cookies = cookie.parse(cookies); // 与 express 中调用 cookie.serialize () 对应,解析 cookie | |
| req.cookies = JSONCookies(req.cookies); // JSON 字符序列转化为 JSON 对象 | 
| //./lib/parse.js | |
|     // 接续 cookie 中的 JSON 字符序列 | |
| exports.JSONCookies = function(obj){ | |
| var cookies = Object.keys(obj); // 获取 obj 对象的 property | |
| var key; | |
| var val; | |
|        // 循环判断并解析 | |
| for (var i = 0; i < cookies.length; i++) { | |
| key = cookies[i]; | |
| val = exports.JSONCookie(obj[key]); | |
|         // 如果是 JSON 字符序列则保存 | |
| if (val) { | |
| obj[key] = val; | |
|         } | |
|       } | |
| return obj; | |
| }; | |
|    // 解析 JSON 字符序列 | |
| exports.JSONCookie = function(str) { | |
| if (!str || str.substr(0, 2) !== 'j:') return; // 判断是否为 JSON 字符序列,如果不是返回 undefined | |
| try { | |
| return JSON.parse(str.slice(2)); // 解析 JSON 字符序列 | |
| } catch (err) { | |
|         // no op | |
|       } | |
| }; | 
・如果使用了 signed 功能,cookie-parser 通过如下代码解析 req.headers.cookie
| //index.js | |
| var cookie = require('cookie'); | |
| var parse = require('./lib/parse'); | |
| if (req.cookies) return next(); // 如果存在 req.cookies 跳过这个 middleware | |
|     // 调用 res.cookie (name, value , {singed: true}),express 会使用 req.secret。故使用了签名功能,需给 cookie-parser 传递 secret,且 res.cookie (name, value , {singed: true}) 需在 cookie-parser 插         | |
|     // 入 express 后再使用 | |
| req.secret = secret; | |
| req.cookies = Object.create(null); | |
| req.signedCookies = Object.create(null); // 创建 req.singedCookies,所有解析后的 signed cookie 都保存在这个对象中,req.cookies 中没有任何 signed cookie | |
|     // 如果请求中没有 cookies | |
| if (!cookies) { | |
| return next(); | |
|     } | |
| req.cookies = cookie.parse(cookies, options); // 与 express 中调用 cookie.serialize () 对应,解析 cookie | |
|     // parse signed cookies | |
| if (secret) { | |
|       // 判断是否为 singed cookie。如果是,则去掉签名,同时删除 req.cookies 中对应的 property,将这些去掉签名的 cookie 组成一个对象,保存在 req.signedCookies 中 | |
| req.signedCookies = parse.signedCookies(req.cookies, secret); | |
|       // JSON 字符序列转化为 JSON 对象 | |
| req.signedCookies = parse.JSONCookies(req.signedCookies); | |
|     } | 
| //./lib/parse.js | |
| var signature = require('cookie-signature'); | |
| exports.signedCookies = function(obj, secret){ | |
| var cookies = Object.keys(obj); // 获取 obj 对象的 property | |
| var dec; | |
| var key; | |
| var ret = Object.create(null); // 创建返回对象 | |
| var val; | |
| for (var i = 0; i < cookies.length; i++) { | |
| key = cookies[i]; | |
| val = obj[key]; | |
| dec = exports.signedCookie(val, secret); | |
|         // 判断是否是去掉签名后的 value,如果是保存该值到 ret 中同时删除 obj 中的相应 property | |
| if (val !== dec) { | |
| ret[key] = dec; | |
| delete obj[key]; | |
|         } | |
|       } | |
| return ret; | |
| }; | |
| exports.signedCookie = function(str, secret){ | |
|         // 判断是否添加了签名,如果添加了签名则去掉签名,否则返回原字符串 | |
| return str.substr(0, 2) === 's:' | |
| ? signature.unsign(str.slice(2), secret) | |
| : str; | |
| }; | 
综上所诉,解析后的 unsigned cookie 保存在 req.cookies 中,而解析后的 signed cookie 只保存在 req.signedCookies 中。使用 cookie-parser 插件,后续代码直接使用 req.cookies 或者 req.signedCookies 即可
