Node.js中的etag包知识学习之express的配置etag

来源:互联网 发布:萧井陌 编程入门指南 编辑:程序博客网 时间:2024/06/09 17:32

 原文地址 :阅读原文

  API为:etag(entity, [options])

   这个方法为给定的实体产生一个strong etag。这个方法必须获取这个实体的完整内容。可以是Buffer,可以是fs.Stats。默认情况下,这个strong etag的产生是不需要fs.Stats的,因为这个fs.Stats会产生weak Etag而不是strong etag,不过这种默认行为可以通过options.weak来覆盖!
   options:

    weak:指定产生的Etag是否会包含弱验证符号(即开头的W/),如果是true那么会包含字符串"W/"。默认是false,除非我们传入的实体是fs.Stats,这时候weak就是true了表示有开头的弱验证符号,而且是通过文件的内容来产生的etag值!

问题1:etag的API中第一个参数是不是必须的?

//必须含有参数  if (entity == null) {    throw new TypeError('argument entity is required')  }
问题2:第一个参数必须是Buffer,fs.Stats或者String?

 // support fs.Stats object  var isStats = isstats(entity)  //判断是否是文件描述符  var weak = options && typeof options.weak === 'boolean'    ? options.weak    : isStats  //如果用户传入了weak,那么用用户的weak为主,否则weak保存的就是是否是文件描述符  // validate argument  if (!isStats && typeof entity !== 'string' && !Buffer.isBuffer(entity)) {    throw new TypeError('argument entity must be string, Buffer, or fs.Stats')  }

很显然,上面的问题的答案yes。那么他是如何判断一个对象是否为文件描述符呢?

function isstats(obj) {  // genuine fs.Stats  //判断是否是原生的文件描述符,通过instanceof判断  if (typeof Stats === 'function' && obj instanceof Stats) {    return true  }  // quack quack  //n. 庸医;鸭叫声 adj. 骗人的;冒牌医生 vi. (鸭子)嘎嘎叫;吹嘘;大声闲聊  //如果不是原生的文件描述符,那么判断这个对象是否有ctime,同时ctime(change time)是Date对象,是否有mtime,同时mtime(modified time)是Date  //是否有ino,而且是number,是否有size同时是number!  return obj && typeof obj === 'object'    && 'ctime' in obj && toString.call(obj.ctime) === '[object Date]'    && 'mtime' in obj && toString.call(obj.mtime) === '[object Date]'    && 'ino' in obj && typeof obj.ino === 'number'    && 'size' in obj && typeof obj.size === 'number'}
很显然是通过instanceof或者判断一个对象的ctime,mtime,ino和size属性来完成的!
问题3:如果是文件描述符和不是文件描述符有什么区别?

var tag = isStats    ? stattag(entity)    : entitytag(entity)  //如果是文件描述符那么用stattag来产生tag,否则用entitytag  return weak    ? 'W/' + tag    : tag
如果是文件描述符那么是通过stattag来完成,否则用entitytag来完成的
 //如果传入了文件描述符那么获取他的mtime(modified time)和size来产生etag,很显然这时候的etag产生不需要文件的内容 //而只是用文件描述符的mtime和size!function stattag(stat) {  var mtime = stat.mtime.getTime().toString(16)  var size = stat.size.toString(16)  return '"' + size + '-' + mtime + '"'}
也就是说如果是文件描述符那么只是用的mtime,size来产生etag
/** * Generate an entity tag. * @param {Buffer|string} entity * @return {string} * @private */function entitytag(entity) {  if (entity.length === 0) {    // fast-path empty    //如果传入的内容为空那么返回的etag为固定值    return '"0-1B2M2Y8AsgTpgAmY7PhCfg"'  }  // compute hash of entity  var hash = crypto    .createHash('md5')    .update(entity, 'utf8')    .digest('base64')    .replace(base64PadCharRegExp, '')   //用文件的内容来产生etag,同时取消其中连续的=号!  // compute length of entity  var len = typeof entity === 'string'    ? Buffer.byteLength(entity, 'utf8')    : entity.length    //如果传入的参数是string那么获取这个string的字节长度,如果不是string那么直接获取他的length属性  return '"' + len.toString(16) + '-' + hash + '"'  //所以返回的值为其长度的16进制编码,同时加上内容的hash值!}
如果不是文件描述符那么这时候是通过"内容的长度的十六进制+文件内容的md5的16进制来完成的"!
问题4:那么是不是如果是文件描述符就返回mtime+size来返回的etag,否则就返回内容长度的十六进制+文件内容的md5的16进制?

<pre name="code" class="javascript">  var weak = options && typeof options.weak === 'boolean'    ? options.weak    : isStats  return weak    ? 'W/' + tag    : tag

很显然会有如下的结论:

              

问题5:为什么要看这部分的原码,为什么要仔细学习etag部分?

这是因为express中的setting中有一个选项就是etag,这个etag的产生就是通过这个包来完成的,我们看看里面他关于etag的配置有那些?


我们看到其中etag可以为布尔值表示是否开启etag,而strong表示开启strong Etag,反之表示weak,当然也可以指定一个函数,这个函数让自己控制对etag的生成!仔细阅读etag

这部分的原码如下:

'use strict'/** * Module exports. * @public */module.exports = etagvar crypto = require('crypto')var Stats = require('fs').Stats//引入node.js的crypto模块和fs模块var base64PadCharRegExp = /=+$/var toString = Object.prototype.toString/** * Generate an entity tag. * @param {Buffer|string} entity * @return {string} * @private */function entitytag(entity) {  if (entity.length === 0) {    // fast-path empty    //如果传入的内容为空那么返回的etag为固定值    return '"0-1B2M2Y8AsgTpgAmY7PhCfg"'  }  // compute hash of entity  var hash = crypto    .createHash('md5')    .update(entity, 'utf8')    .digest('base64')    .replace(base64PadCharRegExp, '')   //用文件的内容来产生etag,同时取消其中连续的=号!  // compute length of entity  var len = typeof entity === 'string'    ? Buffer.byteLength(entity, 'utf8')    : entity.length    //如果传入的参数是string那么获取这个string的字节长度,如果不是string那么直接获取他的length属性  return '"' + len.toString(16) + '-' + hash + '"'  //所以返回的值为其长度的16进制编码,同时加上内容的hash值!}/** * Create a simple ETag. * * @param {string|Buffer|Stats} entity   //第一个参数可以是String,Buffer,Stats * @param {object} [options] //第二个参数是一个对象,其中的weak属性是boolean * @param {boolean} [options.weak] //函数返回值为String * @return {String} * @public */function etag(entity, options) {  //必须含有参数  if (entity == null) {    throw new TypeError('argument entity is required')  }  // support fs.Stats object  var isStats = isstats(entity)  //判断是否是文件描述符  var weak = options && typeof options.weak === 'boolean'    ? options.weak    : isStats  //如果用户传入了weak,那么用用户的weak为主,否则weak保存的就是是否是文件描述符  // validate argument  if (!isStats && typeof entity !== 'string' && !Buffer.isBuffer(entity)) {    throw new TypeError('argument entity must be string, Buffer, or fs.Stats')  }  // generate entity tag  var tag = isStats    ? stattag(entity)    : entitytag(entity)  //如果是文件描述符那么用stattag来产生tag,否则用entitytag  return weak    ? 'W/' + tag    : tag}/** * Determine if object is a Stats object. * * @param {object} obj * @return {boolean} * @api private */function isstats(obj) {  // genuine fs.Stats  //判断是否是原生的文件描述符,通过instanceof判断  if (typeof Stats === 'function' && obj instanceof Stats) {    return true  }  // quack quack  //n. 庸医;鸭叫声 adj. 骗人的;冒牌医生 vi. (鸭子)嘎嘎叫;吹嘘;大声闲聊  //如果不是原生的文件描述符,那么判断这个对象是否有ctime,同时ctime(change time)是Date对象,是否有mtime,同时mtime(modified time)是Date  //是否有ino,而且是number,是否有size同时是number!  return obj && typeof obj === 'object'    && 'ctime' in obj && toString.call(obj.ctime) === '[object Date]'    && 'mtime' in obj && toString.call(obj.mtime) === '[object Date]'    && 'ino' in obj && typeof obj.ino === 'number'    && 'size' in obj && typeof obj.size === 'number'}/** * Generate a tag for a stat. * * @param {object} stat * @return {string} * @private */ //如果传入了文件描述符那么获取他的mtime(modified time)和size来产生etag,很显然这时候的etag产生不需要文件的内容 //而只是用文件描述符的mtime和size!function stattag(stat) {  var mtime = stat.mtime.getTime().toString(16)  var size = stat.size.toString(16)  return '"' + size + '-' + mtime + '"'}

0 0
原创粉丝点击