/**!
* 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);
}
As can be seen from the above code, koa-bodyparser
finally passes through co-body
to parse the request content and generate ctx.req.body
parse.json
, explore the approximate process:
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
method will first determine whether the request needs to be decompressed (inflate), and then parse the request body of the HTTP request:
raw-body
In fact, it is to monitor the data event on this.req. This.req is passed by koa by calling the createServer method of Node's built-in module http when creating a service.
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
});
});
Summarize
koa-bodyparser
essence is still http
the module's parsing of the request body, co-body
also only its upper encapsulation.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。