Use koa-body to handle multipart data

This commit is contained in:
Aurélien Georget 2017-04-07 16:54:32 +02:00
parent 5228c0ef39
commit 63393f88a3
5 changed files with 221 additions and 82 deletions

View File

@ -19,11 +19,10 @@ const mixinAfter = require('./private/after');
* @constructor
*/
// Private properties
// Private properties
let _instance = null;
class Strapi extends EventEmitter {
constructor() {
super();

View File

@ -6,22 +6,13 @@
module.exports = strapi => {
return {
/**
* Default options
*/
defaults: {
parser: {
encode: 'utf-8',
formLimit: '56kb',
jsonLimit: '1mb',
strict: true,
extendTypes: {
json: [
'application/x-javascript'
]
}
multipart: true
}
},
@ -30,13 +21,11 @@ module.exports = strapi => {
*/
initialize: cb => {
strapi.app.use(strapi.middlewares.bodyparser({
encode: strapi.config.parser.encode,
formLimit: strapi.config.parser.formLimit,
jsonLimit: strapi.config.parser.jsonLimit,
strict: strapi.config.parser.strict,
extendTypes: strapi.config.parser.extendTypes
}));
strapi.app.use(
strapi.middlewares.convert(
strapi.middlewares.body(strapi.config.parser)
)
);
cb();
}

View File

@ -22,7 +22,6 @@ const regex = require('strapi-utils').regex;
module.exports = strapi => {
return {
/**
* Default options
*/
@ -52,25 +51,39 @@ module.exports = strapi => {
// and match the controller and action to the desired endpoint.
_.forEach(strapi.config.routes, value => {
if (_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))) {
if (
_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))
) {
return;
}
const endpoint = `${value.method} ${value.path}`;
try {
const {policies, action, validate} = routerChecker(value, endpoint);
const { policies, action, validate } = routerChecker(value, endpoint);
if (_.isUndefined(action) || !_.isFunction(action)) {
return strapi.log.warn('Ignored attempt to bind route `' + endpoint + '` to unknown controller/action.');
return strapi.log.warn(
'Ignored attempt to bind route `' +
endpoint +
'` to unknown controller/action.'
);
}
strapi.router.route(_.omitBy({
method: value.method,
path: value.path,
handler: _.remove([strapi.middlewares.compose(policies), action], o => _.isFunction(o)),
validate
}, _.isEmpty));
strapi.router.route(
_.omitBy(
{
method: value.method,
path: value.path,
handler: _.remove(
[strapi.middlewares.compose(policies), action],
o => _.isFunction(o)
),
validate
},
_.isEmpty
)
);
} catch (err) {
cb(err);
}
@ -81,31 +94,47 @@ module.exports = strapi => {
const routerAdmin = strapi.middlewares.joiRouter();
_.forEach(strapi.admin.config.routes, value => {
if (_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))) {
if (
_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))
) {
return;
}
const endpoint = `${value.method} ${value.path}`;
try {
const {policies, action, validate} = routerChecker(value, endpoint);
const { policies, action, validate } = routerChecker(value, endpoint);
if (_.isUndefined(action) || !_.isFunction(action)) {
return strapi.log.warn('Ignored attempt to bind route `' + endpoint + '` to unknown controller/action.');
return strapi.log.warn(
'Ignored attempt to bind route `' +
endpoint +
'` to unknown controller/action.'
);
}
routerAdmin.route(_.omitBy({
method: value.method,
path: value.path,
handler: _.remove([strapi.middlewares.compose(policies), action], o => _.isFunction(o)),
validate
}, _.isEmpty));
routerAdmin.route(
_.omitBy(
{
method: value.method,
path: value.path,
handler: _.remove(
[strapi.middlewares.compose(policies), action],
o => _.isFunction(o)
),
validate
},
_.isEmpty
)
);
} catch (err) {
cb(err);
}
});
routerAdmin.prefix(strapi.config.admin || `/${strapi.config.paths.admin}`);
routerAdmin.prefix(
strapi.config.admin || `/${strapi.config.paths.admin}`
);
// TODO:
// - Mount on main router `strapi.router.use(routerAdmin.middleware());`
@ -120,29 +149,50 @@ module.exports = strapi => {
const router = strapi.middlewares.joiRouter();
// Exclude routes with prefix.
const excludedRoutes = _.omitBy(value, o => !o.hasOwnProperty('prefix'));
const excludedRoutes = _.omitBy(
value,
o => !o.hasOwnProperty('prefix')
);
// Add others routes to the plugin's router.
_.forEach(_.omit(value, _.keys(excludedRoutes)), value => {
if (_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))) {
if (
_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))
) {
return;
}
const endpoint = `${value.method} ${value.path}`;
try {
const {policies, action, validate} = routerChecker(value, endpoint, plugin);
const { policies, action, validate } = routerChecker(
value,
endpoint,
plugin
);
if (_.isUndefined(action) || !_.isFunction(action)) {
return strapi.log.warn('Ignored attempt to bind route `' + endpoint + '` to unknown controller/action.');
return strapi.log.warn(
'Ignored attempt to bind route `' +
endpoint +
'` to unknown controller/action.'
);
}
router.route(_.omitBy({
method: value.method,
path: value.path,
handler: _.remove([strapi.middlewares.compose(policies), action], o => _.isFunction(o)),
validate
}, _.isEmpty));
router.route(
_.omitBy(
{
method: value.method,
path: value.path,
handler: _.remove(
[strapi.middlewares.compose(policies), action],
o => _.isFunction(o)
),
validate
},
_.isEmpty
)
);
} catch (err) {
cb(err);
}
@ -153,25 +203,44 @@ module.exports = strapi => {
// /!\ Could override main router's routes.
if (!_.isEmpty(excludedRoutes)) {
_.forEach(excludedRoutes, value => {
if (_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))) {
if (
_.isEmpty(_.get(value, 'method')) ||
_.isEmpty(_.get(value, 'path'))
) {
return;
}
const endpoint = `${value.method} ${value.path}`;
try {
const {policies, action, validate} = routerChecker(value, endpoint, plugin);
const { policies, action, validate } = routerChecker(
value,
endpoint,
plugin
);
if (_.isUndefined(action) || !_.isFunction(action)) {
return strapi.log.warn('Ignored attempt to bind route `' + endpoint + '` to unknown controller/action.');
return strapi.log.warn(
'Ignored attempt to bind route `' +
endpoint +
'` to unknown controller/action.'
);
}
strapi.router.route(_.omitBy({
method: value.method,
path: value.path,
handler: _.remove([strapi.middlewares.compose(policies), action], o => _.isFunction(o)),
validate
}, _.isEmpty));
strapi.router.route(
_.omitBy(
{
method: value.method,
path: value.path,
handler: _.remove(
[strapi.middlewares.compose(policies), action],
o => _.isFunction(o)
),
validate
},
_.isEmpty
)
);
} catch (err) {
cb(err);
}
@ -187,11 +256,13 @@ module.exports = strapi => {
// Let the router use our routes and allowed methods.
strapi.app.use(strapi.router.middleware());
strapi.app.use(strapi.router.router.allowedMethods({
throw: false,
notImplemented: () => Boom.notImplemented(),
methodNotAllowed: () => Boom.methodNotAllowed()
}));
strapi.app.use(
strapi.router.router.allowedMethods({
throw: false,
notImplemented: () => Boom.notImplemented(),
methodNotAllowed: () => Boom.methodNotAllowed()
})
);
strapi.app.use(responsesPolicy);
@ -218,7 +289,10 @@ module.exports = strapi => {
// Define controller and action names.
const handler = _.trim(value.handler).split('.');
const controller = plugin ? strapi.plugins[plugin].controllers[handler[0].toLowerCase()] : strapi.controllers[handler[0].toLowerCase()] || strapi.admin.controllers[handler[0].toLowerCase()];
const controller = plugin
? strapi.plugins[plugin].controllers[handler[0].toLowerCase()]
: strapi.controllers[handler[0].toLowerCase()] ||
strapi.admin.controllers[handler[0].toLowerCase()];
const action = controller[handler[1]];
// Retrieve the API's name where the controller is located
@ -235,11 +309,17 @@ module.exports = strapi => {
policies.push(responsesPolicy);
// Allow string instead of array of policies.
if (!_.isArray(_.get(value, 'config.policies')) && !_.isEmpty(_.get(value, 'config.policies'))) {
if (
!_.isArray(_.get(value, 'config.policies')) &&
!_.isEmpty(_.get(value, 'config.policies'))
) {
value.config.policies = [value.config.policies];
}
if (_.isArray(_.get(value, 'config.policies')) && !_.isEmpty(_.get(value, 'config.policies'))) {
if (
_.isArray(_.get(value, 'config.policies')) &&
!_.isEmpty(_.get(value, 'config.policies'))
) {
_.forEach(value.config.policies, policy => {
// Define global policy prefix.
const globalPolicyPrefix = 'global.';
@ -247,33 +327,103 @@ module.exports = strapi => {
const policySplited = policy.split('.');
// Looking for global policy or namespaced.
if (_.startsWith(policy, globalPolicyPrefix, 0) && !_.isEmpty(strapi.config.policies, policy.replace(globalPolicyPrefix, ''))) {
if (
_.startsWith(policy, globalPolicyPrefix, 0) &&
!_.isEmpty(
strapi.config.policies,
policy.replace(globalPolicyPrefix, '')
)
) {
// Global policy.
return policies.push(strapi.config.policies[policy.replace(globalPolicyPrefix, '').toLowerCase()]);
} else if (_.startsWith(policy, pluginPolicyPrefix, 0) && strapi.plugins[policySplited[1]] && !_.isEmpty(_.get(strapi.plugins, policySplited[1] + '.config.policies.' + policySplited[2].toLowerCase()))) {
return policies.push(
strapi.config.policies[
policy.replace(globalPolicyPrefix, '').toLowerCase()
]
);
} else if (
_.startsWith(policy, pluginPolicyPrefix, 0) &&
strapi.plugins[policySplited[1]] &&
!_.isEmpty(
_.get(
strapi.plugins,
policySplited[1] +
'.config.policies.' +
policySplited[2].toLowerCase()
)
)
) {
// Plugin's policies can be used from app APIs with a specific syntax (`plugins.pluginName.policyName`).
return policies.push(_.get(strapi.plugins, policySplited[1] + '.config.policies.' + policySplited[2].toLowerCase()));
} else if (!_.startsWith(policy, globalPolicyPrefix, 0) && plugin && !_.isEmpty(_.get(strapi.plugins, plugin + '.config.policies.' + policy.toLowerCase()))) {
return policies.push(
_.get(
strapi.plugins,
policySplited[1] +
'.config.policies.' +
policySplited[2].toLowerCase()
)
);
} else if (
!_.startsWith(policy, globalPolicyPrefix, 0) &&
plugin &&
!_.isEmpty(
_.get(
strapi.plugins,
plugin + '.config.policies.' + policy.toLowerCase()
)
)
) {
// Plugin policy used in the plugin itself.
return policies.push(_.get(strapi.plugins, plugin + '.config.policies.' + policy.toLowerCase()));
} else if (!_.startsWith(policy, globalPolicyPrefix, 0) && !_.isEmpty(_.get(strapi.api, currentApiName + '.config.policies.' + policy.toLowerCase()))) {
return policies.push(
_.get(
strapi.plugins,
plugin + '.config.policies.' + policy.toLowerCase()
)
);
} else if (
!_.startsWith(policy, globalPolicyPrefix, 0) &&
!_.isEmpty(
_.get(
strapi.api,
currentApiName + '.config.policies.' + policy.toLowerCase()
)
)
) {
// API policy used in the API itself.
return policies.push(_.get(strapi.api, currentApiName + '.config.policies.' + policy.toLowerCase()));
return policies.push(
_.get(
strapi.api,
currentApiName + '.config.policies.' + policy.toLowerCase()
)
);
}
strapi.log.error('Ignored attempt to bind route `' + endpoint + '` with unknown policy `' + policy + '`.');
strapi.log.error(
'Ignored attempt to bind route `' +
endpoint +
'` with unknown policy `' +
policy +
'`.'
);
});
}
// Init validate.
const validate = {};
if (_.isString(_.get(value, 'config.validate')) && !_.isEmpty(_.get(value, 'config.validate'))) {
const validator = _.get(strapi.api, currentApiName + '.validators.' + value.config.validate);
if (
_.isString(_.get(value, 'config.validate')) &&
!_.isEmpty(_.get(value, 'config.validate'))
) {
const validator = _.get(
strapi.api,
currentApiName + '.validators.' + value.config.validate
);
_.merge(validate, _.mapValues(validator, value => {
return builder.build(value);
}));
_.merge(
validate,
_.mapValues(validator, value => {
return builder.build(value);
})
);
}
return {

View File

@ -34,13 +34,14 @@
},
"dependencies": {
"async": "^2.1.2",
"await-busboy": "^1.0.1",
"boom": "^4.2.0",
"consolidate": "~0.14.0",
"delegates": "^1.0.0",
"kcors": "^2.2.0",
"koa": "^2.1.0",
"koa-better-static": "^1.0.5",
"koa-bodyparser": "^4.1.0",
"koa-body": "^2.0.0",
"koa-compose": "^3.2.1",
"koa-compress": "^2.0.0",
"koa-convert": "^1.2.0",

View File

@ -16,7 +16,7 @@ describe('middlewares', () => {
});
it('`strapi.middlewares.bodyparser` should be a function', () => {
assert(typeof strapi.middlewares.bodyparser === 'function');
assert(typeof strapi.middlewares.body === 'function');
});
it('`strapi.middlewares.compose` should be a function', () => {