Skip to content

Commit 8715c66

Browse files
committed
generic body parser
Co-authored-by: S Dellysse <[email protected]> Co-authored-by: ctcpip <[email protected]> squashed the following commits: Added support for external parsers to bodyParser.json() removed test dependency on json-bigint reworked doc to describe json parser() func better added parser() option and doc for .text() added parser() option and doc for .raw() added parser() option and doc for .urlencoded() cleanup to satisfy linter added generic parser converted json parser to use generic parser converted raw parser to use generic parser converted text parser to use generic parser converted urlencoded parser to use generic parser cleanup / fix linter warnings removed items from README added bodyParser.generic() getter cleanup / fix linter warnings fixed tests after rebase satisfying linter Ref'd genParser via the bodyparser getter to signal how third party parsers should import genParser' removed dep on object-assign, which didnt support node < 0.10 minor text cleanup 🔧 add debug script 🐛 fix object merging 🔥 clean up 💚 remove node < 4 from CI
1 parent ee8bd68 commit 8715c66

File tree

9 files changed

+263
-391
lines changed

9 files changed

+263
-391
lines changed

README.md

+24-4
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,27 @@ specifies the number of bytes; if it is a string, the value is passed to the
8888
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
8989
to `'100kb'`.
9090

91+
##### parser
92+
93+
The `parser` option is the function called against the request body to convert
94+
it to a Javascript object. If a `reviver` is supplied, it is supplied as the
95+
second argument to this function.
96+
97+
```
98+
parser(body, reviver) -> req.body
99+
```
100+
101+
Defaults to `JSON.parse`.
102+
91103
##### reviver
92104

93-
The `reviver` option is passed directly to `JSON.parse` as the second
94-
argument. You can find more information on this argument
105+
You can find more information on this argument
95106
[in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter).
96107

97108
##### strict
98109

99110
When set to `true`, will only accept arrays and objects; when `false` will
100-
accept anything `JSON.parse` accepts. Defaults to `true`.
111+
accept anything the `parser` accepts. Defaults to `true`.
101112

102113
##### type
103114

@@ -290,11 +301,20 @@ of `✓`. Defaults to `false`.
290301
Whether to decode numeric entities such as `&#9786;` when parsing an iso-8859-1
291302
form. Defaults to `false`.
292303

293-
294304
#### depth
295305

296306
The `depth` option is used to configure the maximum depth of the `qs` library when `extended` is `true`. This allows you to limit the amount of keys that are parsed and can be useful to prevent certain types of abuse. Defaults to `32`. It is recommended to keep this value as low as possible.
297307

308+
##### parser
309+
310+
The `parser` option, if supplied, is used to in place of the default parser to
311+
convert the request body into a Javascript object. If this option is supplied,
312+
both the `extended` and `parameterLimit` options are ignored.
313+
314+
```
315+
parser(body) -> req.body
316+
```
317+
298318
## Errors
299319

300320
The middlewares provided by this module create errors using the

lib/generic-parser.js

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*!
2+
* body-parser
3+
* Copyright(c) 2014 Jonathan Ong
4+
* Copyright(c) 2014-2015 Douglas Christopher Wilson
5+
* MIT Licensed
6+
*/
7+
8+
'use strict'
9+
10+
/**
11+
* Module dependencies.
12+
* @private
13+
*/
14+
15+
var bytes = require('bytes')
16+
var contentType = require('content-type')
17+
var createError = require('http-errors')
18+
var debug = require('debug')('body-parser:generic')
19+
var isFinished = require('on-finished').isFinished
20+
var read = require('./read')
21+
var typeis = require('type-is')
22+
23+
/**
24+
* Module exports.
25+
*/
26+
27+
module.exports = generic
28+
29+
/**
30+
* Use this to create a middleware that parses request bodies
31+
*
32+
* @param {object} [options]
33+
* @return {function}
34+
* @public
35+
*/
36+
37+
function generic (parserOptions, parserOverrides) {
38+
// Squash the options and the overrides down into one object
39+
var opts = Object.create(parserOptions)
40+
Object.assign(opts, parserOverrides)
41+
42+
var limit = typeof opts.limit !== 'number'
43+
? bytes.parse(opts.limit || '100kb')
44+
: opts.limit
45+
var charset = opts.charset
46+
var inflate = opts.inflate !== false
47+
var verify = opts.verify || false
48+
var parse = opts.parse || defaultParse
49+
var defaultReqCharset = opts.defaultCharset || 'utf-8'
50+
var type = opts.type
51+
52+
if (verify !== false && typeof verify !== 'function') {
53+
throw new TypeError('option verify must be function')
54+
}
55+
56+
// create the appropriate type checking function
57+
var shouldParse = typeof type !== 'function'
58+
? typeChecker(type)
59+
: type
60+
61+
// create the appropriate charset validating function
62+
var validCharset = typeof charset !== 'function'
63+
? charsetValidator(charset)
64+
: charset
65+
66+
return function genericParser (req, res, next) {
67+
if (isFinished(req)) {
68+
debug('body already parsed')
69+
next()
70+
return
71+
}
72+
73+
if (!('body' in req)) {
74+
req.body = undefined
75+
}
76+
77+
// skip requests without bodies
78+
if (!typeis.hasBody(req)) {
79+
debug('skip empty body')
80+
next()
81+
return
82+
}
83+
84+
debug('content-type %j', req.headers['content-type'])
85+
86+
// determine if request should be parsed
87+
if (!shouldParse(req)) {
88+
debug('skip parsing')
89+
next()
90+
return
91+
}
92+
93+
// assert charset per RFC 7159 sec 8.1
94+
var reqCharset = null
95+
if (charset !== undefined) {
96+
reqCharset = getCharset(req) || defaultReqCharset
97+
if (!validCharset(reqCharset)) {
98+
debug('invalid charset')
99+
next(createError(415, 'unsupported charset "' + reqCharset.toUpperCase() + '"', {
100+
charset: reqCharset,
101+
type: 'charset.unsupported'
102+
}))
103+
return
104+
}
105+
}
106+
107+
// read
108+
read(req, res, next, parse, debug, {
109+
encoding: reqCharset,
110+
inflate: inflate,
111+
limit: limit,
112+
verify: verify
113+
})
114+
}
115+
}
116+
117+
function defaultParse (buf) {
118+
return buf
119+
}
120+
121+
/**
122+
* Get the charset of a request.
123+
*
124+
* @param {object} req
125+
* @api private
126+
*/
127+
128+
function getCharset (req) {
129+
try {
130+
return (contentType.parse(req).parameters.charset || '').toLowerCase()
131+
} catch (e) {
132+
return undefined
133+
}
134+
}
135+
136+
/**
137+
* Get the simple type checker.
138+
*
139+
* @param {string} type
140+
* @return {function}
141+
*/
142+
143+
function typeChecker (type) {
144+
return function checkType (req) {
145+
return Boolean(typeis(req, type))
146+
}
147+
}
148+
149+
/**
150+
* Get the simple charset validator.
151+
*
152+
* @param {string} type
153+
* @return {function}
154+
*/
155+
156+
function charsetValidator (charset) {
157+
return function validateCharset (reqCharset) {
158+
return charset === reqCharset
159+
}
160+
}

0 commit comments

Comments
 (0)