/**
|
* koa-body - index.js
|
* Copyright(c) 2014
|
* MIT Licensed
|
*
|
* @author Daryl Lau (@dlau)
|
* @author Charlike Mike Reagent (@tunnckoCore)
|
* @api private
|
*/
|
|
'use strict';
|
|
/**
|
* Module dependencies.
|
*/
|
|
const buddy = require('co-body');
|
const forms = require('formidable');
|
const symbolUnparsed = require('./unparsed.js');
|
|
/**
|
* Expose `requestbody()`.
|
*/
|
|
module.exports = requestbody;
|
|
const jsonTypes = [
|
'application/json',
|
'application/json-patch+json',
|
'application/vnd.api+json',
|
'application/csp-report'
|
];
|
|
/**
|
*
|
* @param {Object} options
|
* @see https://github.com/dlau/koa-body
|
* @api public
|
*/
|
function requestbody(opts) {
|
opts = opts || {};
|
opts.onError = 'onError' in opts ? opts.onError : false;
|
opts.patchNode = 'patchNode' in opts ? opts.patchNode : false;
|
opts.patchKoa = 'patchKoa' in opts ? opts.patchKoa : true;
|
opts.multipart = 'multipart' in opts ? opts.multipart : false;
|
opts.urlencoded = 'urlencoded' in opts ? opts.urlencoded : true;
|
opts.json = 'json' in opts ? opts.json : true;
|
opts.text = 'text' in opts ? opts.text : true;
|
opts.encoding = 'encoding' in opts ? opts.encoding : 'utf-8';
|
opts.jsonLimit = 'jsonLimit' in opts ? opts.jsonLimit : '1mb';
|
opts.jsonStrict = 'jsonStrict' in opts ? opts.jsonStrict : true;
|
opts.formLimit = 'formLimit' in opts ? opts.formLimit : '56kb';
|
opts.queryString = 'queryString' in opts ? opts.queryString : null;
|
opts.formidable = 'formidable' in opts ? opts.formidable : {};
|
opts.includeUnparsed = 'includeUnparsed' in opts ? opts.includeUnparsed : false
|
opts.textLimit = 'textLimit' in opts ? opts.textLimit : '56kb';
|
|
// @todo: next major version, opts.strict support should be removed
|
if (opts.strict && opts.parsedMethods) {
|
throw new Error('Cannot use strict and parsedMethods options at the same time.')
|
}
|
|
if ('strict' in opts) {
|
console.warn('DEPRECATED: opts.strict has been deprecated in favor of opts.parsedMethods.')
|
if (opts.strict) {
|
opts.parsedMethods = ['POST', 'PUT', 'PATCH']
|
} else {
|
opts.parsedMethods = ['POST', 'PUT', 'PATCH', 'GET', 'HEAD', 'DELETE']
|
}
|
}
|
|
opts.parsedMethods = 'parsedMethods' in opts ? opts.parsedMethods : ['POST', 'PUT', 'PATCH']
|
opts.parsedMethods = opts.parsedMethods.map(function (method) { return method.toUpperCase() })
|
|
return function (ctx, next) {
|
var bodyPromise;
|
// only parse the body on specifically chosen methods
|
if (opts.parsedMethods.includes(ctx.method.toUpperCase())) {
|
try {
|
if (opts.json && ctx.is(jsonTypes)) {
|
bodyPromise = buddy.json(ctx, {
|
encoding: opts.encoding,
|
limit: opts.jsonLimit,
|
strict: opts.jsonStrict,
|
returnRawBody: opts.includeUnparsed
|
});
|
} else if (opts.urlencoded && ctx.is('urlencoded')) {
|
bodyPromise = buddy.form(ctx, {
|
encoding: opts.encoding,
|
limit: opts.formLimit,
|
queryString: opts.queryString,
|
returnRawBody: opts.includeUnparsed
|
});
|
} else if (opts.text && ctx.is('text/*')) {
|
bodyPromise = buddy.text(ctx, {
|
encoding: opts.encoding,
|
limit: opts.textLimit,
|
returnRawBody: opts.includeUnparsed
|
});
|
} else if (opts.multipart && ctx.is('multipart')) {
|
bodyPromise = formy(ctx, opts.formidable);
|
}
|
} catch (parsingError) {
|
if (typeof opts.onError === 'function') {
|
opts.onError(parsingError, ctx);
|
} else {
|
throw parsingError;
|
}
|
}
|
}
|
|
bodyPromise = bodyPromise || Promise.resolve({});
|
return bodyPromise.catch(function(parsingError) {
|
if (typeof opts.onError === 'function') {
|
opts.onError(parsingError, ctx);
|
} else {
|
throw parsingError;
|
}
|
return next();
|
})
|
.then(function(body) {
|
if (opts.patchNode) {
|
if (isMultiPart(ctx, opts)) {
|
ctx.req.body = body.fields;
|
ctx.req.files = body.files;
|
} else if (opts.includeUnparsed) {
|
ctx.req.body = body.parsed || {};
|
if (! ctx.is('text/*')) {
|
ctx.req.body[symbolUnparsed] = body.raw;
|
}
|
} else {
|
ctx.req.body = body;
|
}
|
}
|
if (opts.patchKoa) {
|
if (isMultiPart(ctx, opts)) {
|
ctx.request.body = body.fields;
|
ctx.request.files = body.files;
|
} else if (opts.includeUnparsed) {
|
ctx.request.body = body.parsed || {};
|
if (! ctx.is('text/*')) {
|
ctx.request.body[symbolUnparsed] = body.raw;
|
}
|
} else {
|
ctx.request.body = body;
|
}
|
}
|
return next();
|
})
|
};
|
}
|
|
/**
|
* Check if multipart handling is enabled and that this is a multipart request
|
*
|
* @param {Object} ctx
|
* @param {Object} opts
|
* @return {Boolean} true if request is multipart and being treated as so
|
* @api private
|
*/
|
function isMultiPart(ctx, opts) {
|
return opts.multipart && ctx.is('multipart');
|
}
|
|
/**
|
* Donable formidable
|
*
|
* @param {Stream} ctx
|
* @param {Object} opts
|
* @return {Promise}
|
* @api private
|
*/
|
function formy(ctx, opts) {
|
return new Promise(function (resolve, reject) {
|
var fields = {};
|
var files = {};
|
var form = new forms.IncomingForm(opts);
|
form.on('end', function () {
|
return resolve({
|
fields: fields,
|
files: files
|
});
|
}).on('error', function (err) {
|
return reject(err);
|
}).on('field', function (field, value) {
|
if (fields[field]) {
|
if (Array.isArray(fields[field])) {
|
fields[field].push(value);
|
} else {
|
fields[field] = [fields[field], value];
|
}
|
} else {
|
fields[field] = value;
|
}
|
}).on('file', function (field, file) {
|
if (files[field]) {
|
if (Array.isArray(files[field])) {
|
files[field].push(file);
|
} else {
|
files[field] = [files[field], file];
|
}
|
} else {
|
files[field] = file;
|
}
|
});
|
if (opts.onFileBegin) {
|
form.on('fileBegin', opts.onFileBegin);
|
}
|
form.parse(ctx.req);
|
});
|
}
|