@koa/bodyparser
Version:
Koa body parsing middleware
143 lines (138 loc) • 4.59 kB
JavaScript
// src/body-parser.ts
import parser from "co-body";
// src/body-parser.utils.ts
import deepMerge from "lodash.merge";
import typeis from "type-is";
// src/body-parser.types.ts
var supportedBodyTypes = ["json", "form", "text", "xml"];
// src/body-parser.utils.ts
var UnsupportedBodyTypeError = class extends Error {
constructor(wrongType) {
super();
this.name = "UnsupportedBodyTypeError";
this.message = `Invalid enabled type '${wrongType}'. make sure to pass an array contains supported types ([${supportedBodyTypes}]).`;
}
};
function getIsEnabledBodyAs(enableTypes) {
for (const enabledType of enableTypes) {
if (!supportedBodyTypes.includes(enabledType)) {
throw new UnsupportedBodyTypeError(enabledType);
}
}
const isEnabledBodyAs = supportedBodyTypes.reduce(
(prevResult, currentType) => ({
...prevResult,
[currentType]: enableTypes.includes(currentType)
}),
{}
);
return isEnabledBodyAs;
}
function getMimeTypes(extendTypes) {
for (const extendedTypeKey of Object.keys(extendTypes)) {
const extendedType = extendTypes[extendedTypeKey];
if (!supportedBodyTypes.includes(extendedTypeKey) || !Array.isArray(extendedType)) {
throw new UnsupportedBodyTypeError(extendedTypeKey);
}
}
const defaultMimeTypes = {
// default json mime types
json: [
"application/json",
"application/json-patch+json",
"application/vnd.api+json",
"application/csp-report",
"application/reports+json",
"application/scim+json"
],
// default form mime types
form: ["application/x-www-form-urlencoded"],
// default text mime types
text: ["text/plain"],
// default xml mime types
xml: ["text/xml", "application/xml"]
};
const mimeTypes = deepMerge(defaultMimeTypes, extendTypes);
return mimeTypes;
}
function isTypes(contentTypeValue, types) {
if (typeof contentTypeValue === "string") {
contentTypeValue = contentTypeValue.replace(/;$/, "");
}
return typeis.is(contentTypeValue, types);
}
// src/body-parser.ts
function bodyParserWrapper(opts = {}) {
const {
patchNode = false,
parsedMethods = ["POST", "PUT", "PATCH"],
detectJSON,
onError,
enableTypes = ["json", "form"],
extendTypes = {},
enableRawChecking = false,
...restOpts
} = opts;
const isEnabledBodyAs = getIsEnabledBodyAs(enableTypes);
const mimeTypes = getMimeTypes(extendTypes);
async function parseBody(ctx) {
const shouldParseBodyAs = (type) => {
return Boolean(
isEnabledBodyAs[type] && isTypes(ctx.request.get("content-type"), mimeTypes[type])
);
};
const bodyType = (() => {
if (detectJSON?.(ctx) || shouldParseBodyAs("json")) return "json";
if (shouldParseBodyAs("form")) return "form";
if (shouldParseBodyAs("text") || shouldParseBodyAs("xml")) return "text";
return null;
})();
if (!bodyType) return {};
const parserOptions = {
// force co-body return raw body
returnRawBody: true,
strict: bodyType === "json" ? restOpts.jsonStrict : void 0,
[`${bodyType}Types`]: mimeTypes[bodyType],
limit: restOpts[`${shouldParseBodyAs("xml") ? "xml" : bodyType}Limit`],
// eslint-disable-next-line unicorn/text-encoding-identifier-case
encoding: restOpts.encoding || "utf-8"
};
return parser[bodyType](ctx, parserOptions);
}
return async function bodyParser(ctx, next) {
if (
// method souldn't be parsed
!parsedMethods.includes(ctx.method.toUpperCase()) || // patchNode enabled and raw request already parsed
patchNode && ctx.req.body !== void 0 || // koa request body already parsed
ctx.request.body !== void 0 || // bodyparser disabled
ctx.disableBodyParser
)
return next();
if (enableRawChecking && ctx.req.body !== void 0) {
ctx.request.body = ctx.req.body;
return next();
}
if (ctx.req.closed) {
ctx.status = 499;
ctx.body = "Request already closed";
return;
}
try {
const response = await parseBody(ctx);
if (patchNode) {
ctx.req.body = "parsed" in response ? response.parsed : {};
if (ctx.req.rawBody === void 0) ctx.req.rawBody = response.raw;
}
ctx.request.body = "parsed" in response ? response.parsed : {};
if (ctx.request.rawBody === void 0) ctx.request.rawBody = response.raw;
} catch (err) {
if (!onError) throw err;
onError(err, ctx);
}
return next();
};
}
export {
bodyParserWrapper as bodyParser,
bodyParserWrapper as default
};