/**! * koa-body-parser - index.js * Copyright(c) 2014 * MIT Licensed * * Authors: *   dead_horse <dead_horse@qq.com> (http://deadhorse.me) *   fengmk2 <m@fengmk2.com> (http://fengmk2.com) */'use strict';/** * Module dependencies. */var parse = require('co-body');var copy = require('copy-to');/** * @param [Object] opts *   - {String} jsonLimit default '1mb' *   - {String} formLimit default '56kb' *   - {string} encoding default 'utf-8' *   - {Object} extendTypes */module.exports = function (opts) {  opts = opts || {};  var detectJSON = opts.detectJSON;  var onerror = opts.onerror;  var enableTypes = opts.enableTypes || ['json', 'form'];  var enableForm = checkEnable(enableTypes, 'form');  var enableJson = checkEnable(enableTypes, 'json');  var enableText = checkEnable(enableTypes, 'text');  var enableXml = checkEnable(enableTypes, 'xml');  opts.detectJSON = undefined;  opts.onerror = undefined;  // force co-body return raw body  opts.returnRawBody = true;  // default json types  var jsonTypes = [    'application/json',    'application/json-patch+json',    'application/vnd.api+json',    'application/csp-report',  ];  // default form types  var formTypes = [    'application/x-www-form-urlencoded',  ];  // default text types  var textTypes = [    'text/plain',  ];  // default xml types  var xmlTypes = [    'text/xml',    'application/xml',  ];  var jsonOpts = formatOptions(opts, 'json');  var formOpts = formatOptions(opts, 'form');  var textOpts = formatOptions(opts, 'text');  var xmlOpts = formatOptions(opts, 'xml');  var extendTypes = opts.extendTypes || {};  extendType(jsonTypes, extendTypes.json);  extendType(formTypes, extendTypes.form);  extendType(textTypes, extendTypes.text);  extendType(xmlTypes, extendTypes.xml);  return async function bodyParser(ctx, next) {    // 判断ctx.request.body是否曾经被设置过,如果是则间接通过    if (ctx.request.body !== undefined) return await next();    // disableBodyParser 为True,间接通过不解析,见API    if (ctx.disableBodyParser) return await next();    try {      // 解析body的入口      const res = await parseBody(ctx);      ctx.request.body = 'parsed' in res ? res.parsed : {};      if (ctx.request.rawBody === undefined) ctx.request.rawBody = res.raw;    } catch (err) {      if (onerror) {        onerror(err, ctx);      } else {        throw err;      }    }    await next();  };  // 别离对json、from、text、xml四种格局进行解析,其余的间接返回{}  async function parseBody(ctx) {    // enableJson 是否开启JSON解析, option 参数配置    // detectJSON 自定义检测是否为JSON申请 option 参数配置    // Content-Type是否为JSON相干    if (enableJson && ((detectJSON && detectJSON(ctx)) || ctx.request.is(jsonTypes))) {      // 最终还是应用co-body去解析      return await parse.json(ctx, jsonOpts);    }    if (enableForm && ctx.request.is(formTypes)) {      return await parse.form(ctx, formOpts);    }    if (enableText && ctx.request.is(textTypes)) {      return await parse.text(ctx, textOpts) || '';    }    if (enableXml && ctx.request.is(xmlTypes)) {      return await parse.text(ctx, xmlOpts) || '';    }    return {};  }};function formatOptions(opts, type) {  var res = {};  copy(opts).to(res);  res.limit = opts[type + 'Limit'];  return res;}function extendType(original, extend) {  if (extend) {    if (!Array.isArray(extend)) {      extend = [extend];    }    extend.forEach(function (extend) {      original.push(extend);    });  }}function checkEnable(types, type) {  return types.includes(type);}

由下面的代码能够看出,koa-bodyparser最终还是通过co-body去解析申请内容并生成ctx.req.body.上面以parse.json为例,探索下大略过程:

const raw = require('raw-body');const inflate = require('inflation');module.exports = async function(req, opts) {  req = req.req || req;  opts = utils.clone(opts);  // defaults  const len = req.headers['content-length'];  const encoding = req.headers['content-encoding'] || 'identity';  if (len && encoding === 'identity') opts.length = ~~len;  opts.encoding = opts.encoding || 'utf8';  opts.limit = opts.limit || '1mb';  const strict = opts.strict !== false;  // 外围代码(重点):  const str = await raw(inflate(req), opts);  try {    const parsed = parse(str);    return opts.returnRawBody ? { parsed, raw: str } : parsed;  } catch (err) {    err.status = 400;    err.body = str;    throw err;  }  function parse(str) {    if (!strict) return str ? JSON.parse(str) : str;    // strict mode always return object    if (!str) return {};    // strict JSON test    if (!strictJSONReg.test(str)) {      throw new SyntaxError('invalid JSON, only supports object and array');    }    return JSON.parse(str);  }};

json办法会先判断是否须要对申请进行解压缩(inflate),而后再进行对HTTP申请的申请体进行解析:

raw-body其实就是监听this.req上的data事件,this.req是koa在创立服务时,调用了Node内置模块http的createServer办法传递的

const server = http.createServer((req, res) => {  // we can access HTTP headers  req.on('data', chunk => {    console.log(`Data chunk available: ${chunk}`);  });  req.on('end', () => {    // end of data  });});

总结

koa-bodyparser实质还是http模块对申请体的解析,co-body也只的它的下层封装。