-
Notifications
You must be signed in to change notification settings - Fork 118
/
Copy pathbody-parser.ts
113 lines (103 loc) · 3.42 KB
/
body-parser.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import parser from 'co-body';
import type * as Koa from 'koa';
import type {BodyParserOptions, BodyType} from './body-parser.types';
import {getIsEnabledBodyAs, getMimeTypes, isTypes} from './body-parser.utils';
/**
* Global declaration for the added properties to the 'ctx.request'
*/
declare module 'koa' {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface Request {
body?: any;
rawBody: string;
}
}
declare module 'http' {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface IncomingMessage {
body?: any;
rawBody: string;
}
}
/**
* Middleware wrapper which delegate options to the core code
*/
export function bodyParserWrapper(opts: BodyParserOptions = {}) {
const {
patchNode = false,
parsedMethods = ['POST', 'PUT', 'PATCH'],
detectJSON,
onError,
enableTypes = ['json', 'form'],
extendTypes = {} as NonNullable<BodyParserOptions['extendTypes']>,
enableRawChecking = false,
...restOpts
} = opts;
const isEnabledBodyAs = getIsEnabledBodyAs(enableTypes);
const mimeTypes = getMimeTypes(extendTypes);
/**
* Handler to parse the request coming data
*/
async function parseBody(ctx: Koa.Context) {
const shouldParseBodyAs = (type: BodyType) => {
return Boolean(
isEnabledBodyAs[type] &&
isTypes(ctx.request.get('content-type'), mimeTypes[type]),
);
};
const bodyType =
detectJSON?.(ctx) || shouldParseBodyAs('json')
? 'json'
: shouldParseBodyAs('form')
? 'form'
: shouldParseBodyAs('text') || shouldParseBodyAs('xml')
? 'text'
: null;
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
if (!bodyType) return {} as Record<string, string>;
const parserOptions = {
// force co-body return raw body
returnRawBody: true,
strict: bodyType === 'json' ? restOpts.jsonStrict : undefined,
[`${bodyType}Types`]: mimeTypes[bodyType],
limit: restOpts[`${shouldParseBodyAs('xml') ? 'xml' : bodyType}Limit`],
};
return parser[bodyType](ctx, parserOptions) as Promise<
Record<string, string>
>;
}
return async function (ctx: Koa.Context, next: Koa.Next) {
if (
// method souldn't be parsed
!parsedMethods.includes(ctx.method.toUpperCase()) ||
// patchNode enabled and raw request already parsed
(patchNode && ctx.req.body !== undefined) ||
// koa request body already parsed
ctx.request.body !== undefined ||
// bodyparser disabled
ctx.disableBodyParser
)
return next();
// raw request parsed and contain 'body' values and it's enabled to override the koa request
if (enableRawChecking && ctx.req.body !== undefined) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
ctx.request.body = ctx.req.body;
return next();
}
try {
const response = await parseBody(ctx);
// patch node
if (patchNode) {
ctx.req.body = 'parsed' in response ? response.parsed : {};
if (ctx.req.rawBody === undefined) ctx.req.rawBody = response.raw;
}
// patch koa
ctx.request.body = 'parsed' in response ? response.parsed : {};
if (ctx.request.rawBody === undefined) ctx.request.rawBody = response.raw;
} catch (err: unknown) {
if (!onError) throw err;
onError(err as Error, ctx);
}
return next();
};
}