diff --git a/README.md b/README.md index 365c81cb..067f80be 100644 --- a/README.md +++ b/README.md @@ -86,16 +86,27 @@ specifies the number of bytes; if it is a string, the value is passed to the [bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults to `'100kb'`. +##### parser + +The `parser` option is the function called against the request body to convert +it to a Javascript object. If a `reviver` is supplied, it is supplied as the +second argument to this function. + +``` +parser(body, reviver) -> req.body +``` + +Defaults to `JSON.parse`. + ##### reviver -The `reviver` option is passed directly to `JSON.parse` as the second -argument. You can find more information on this argument +You can find more information on this argument [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). ##### strict When set to `true`, will only accept arrays and objects; when `false` will -accept anything `JSON.parse` accepts. Defaults to `true`. +accept anything the `parser` accepts. Defaults to `true`. ##### type @@ -159,6 +170,15 @@ The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)` where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. +##### parser + +The `parser` option, if supplied, is used to transform the body of a request +before being set to `req.body`. + +``` +parser(body) -> req.body +``` + ### bodyParser.text([options]) Returns middleware that parses all bodies as a string and only looks at @@ -208,6 +228,15 @@ The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)` where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. +##### parser + +The `parser` option, if supplied, is used to transform the body of a request +before being set to `req.body`. + +``` +parser(body) -> req.body +``` + ### bodyParser.urlencoded([options]) Returns middleware that only parses `urlencoded` bodies and only looks at @@ -274,6 +303,16 @@ The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)` where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. +##### parser + +The `parser` option, if supplied, is used to in place of the default parser to +convert the request body into a Javascript object. If this option is supplied, +both the `extended` and `parameterLimit` options are ignored. + +``` +parser(body) -> req.body +``` + ## Errors The middlewares provided by this module create errors depending on the error diff --git a/lib/types/json.js b/lib/types/json.js index a7bc838c..bae68e5b 100644 --- a/lib/types/json.js +++ b/lib/types/json.js @@ -58,6 +58,7 @@ function json (options) { var strict = opts.strict !== false var type = opts.type || 'application/json' var verify = opts.verify || false + var parser = opts.parser || JSON.parse if (verify !== false && typeof verify !== 'function') { throw new TypeError('option verify must be function') @@ -80,13 +81,13 @@ function json (options) { if (first !== '{' && first !== '[') { debug('strict violation') - throw createStrictSyntaxError(body, first) + throw createStrictSyntaxError(parser, reviver, body, first) } } try { debug('parse json') - return JSON.parse(body, reviver) + return parser(body, reviver) } catch (e) { throw normalizeJsonSyntaxError(e, { stack: e.stack @@ -149,12 +150,12 @@ function json (options) { * @private */ -function createStrictSyntaxError (str, char) { +function createStrictSyntaxError (parser, reviver, str, char) { var index = str.indexOf(char) var partial = str.substring(0, index) + '#' try { - JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation') + parser(partial, reviver); /* istanbul ignore next */ throw new SyntaxError('strict violation') } catch (e) { return normalizeJsonSyntaxError(e, { message: e.message.replace('#', char), diff --git a/lib/types/raw.js b/lib/types/raw.js index f5d1b674..a32ff114 100644 --- a/lib/types/raw.js +++ b/lib/types/raw.js @@ -38,6 +38,7 @@ function raw (options) { : opts.limit var type = opts.type || 'application/octet-stream' var verify = opts.verify || false + var parser = opts.parser || defaultParser if (verify !== false && typeof verify !== 'function') { throw new TypeError('option verify must be function') @@ -49,7 +50,7 @@ function raw (options) { : type function parse (buf) { - return buf + return parser(buf) } return function rawParser (req, res, next) { @@ -87,6 +88,10 @@ function raw (options) { } } +function defaultParser (buf) { + return buf +} + /** * Get the simple type checker. * diff --git a/lib/types/text.js b/lib/types/text.js index 083a0090..451a2ef8 100644 --- a/lib/types/text.js +++ b/lib/types/text.js @@ -40,6 +40,7 @@ function text (options) { : opts.limit var type = opts.type || 'text/plain' var verify = opts.verify || false + var parser = opts.parser || defaultParser if (verify !== false && typeof verify !== 'function') { throw new TypeError('option verify must be function') @@ -51,7 +52,7 @@ function text (options) { : type function parse (buf) { - return buf + return parser(buf) } return function textParser (req, res, next) { @@ -92,6 +93,10 @@ function text (options) { } } +function defaultParser (buf) { + return buf +} + /** * Get the charset of a request. * diff --git a/lib/types/urlencoded.js b/lib/types/urlencoded.js index 5ccda218..acea3bce 100644 --- a/lib/types/urlencoded.js +++ b/lib/types/urlencoded.js @@ -44,7 +44,7 @@ function urlencoded (options) { var opts = options || {} // notice because option default will flip in next major - if (opts.extended === undefined) { + if (opts.extended === undefined && opts.parser === undefined) { deprecate('undefined extended: provide extended option') } @@ -61,9 +61,11 @@ function urlencoded (options) { } // create the appropriate query parser - var queryparse = extended + var parser = opts.parser || ( + extended ? extendedparser(opts) : simpleparser(opts) + ) // create the appropriate type checking function var shouldParse = typeof type !== 'function' @@ -72,7 +74,7 @@ function urlencoded (options) { function parse (body) { return body.length - ? queryparse(body) + ? parser(body) : {} } diff --git a/test/json.js b/test/json.js index 5cc10cf3..ae3d71f4 100644 --- a/test/json.js +++ b/test/json.js @@ -69,6 +69,18 @@ describe('bodyParser.json()', function () { .expect(200, '{"user":"tobi"}', done) }) + it('should use external parsers', function (done) { + request(createServer({ + parser: function (body) { + return { foo: 'bar' } + } + })) + .post('/') + .set('Content-Type', 'application/json') + .send('{"str":') + .expect(200, '{"foo":"bar"}', done) + }) + describe('when JSON is invalid', function () { before(function () { this.server = createServer() diff --git a/test/raw.js b/test/raw.js index cde33224..1cd3a7b3 100644 --- a/test/raw.js +++ b/test/raw.js @@ -19,6 +19,16 @@ describe('bodyParser.raw()', function () { .expect(200, 'buf:746865207573657220697320746f6269', done) }) + it('should parse application/octet-stream with a custom parser', function (done) { + request(createServer({ + parser: function (body) { return body.toString('utf8') } + })) + .post('/') + .set('Content-Type', 'application/octet-stream') + .send('the user is tobi') + .expect(200, '"the user is tobi"', done) + }) + it('should 400 when invalid content-length', function (done) { var rawParser = bodyParser.raw() var server = createServer(function (req, res, next) { diff --git a/test/text.js b/test/text.js index fd4f0aab..1440927c 100644 --- a/test/text.js +++ b/test/text.js @@ -19,6 +19,16 @@ describe('bodyParser.text()', function () { .expect(200, '"user is tobi"', done) }) + it('should parse text/plain with a custom parser', function (done) { + request(createServer({ + parser: function (input) { return input.toUpperCase() } + })) + .post('/') + .set('Content-Type', 'text/plain') + .send('user is tobi') + .expect(200, '"USER IS TOBI"', done) + }) + it('should 400 when invalid content-length', function (done) { var textParser = bodyParser.text() var server = createServer(function (req, res, next) { diff --git a/test/urlencoded.js b/test/urlencoded.js index 38f85d7e..1f0b2cb2 100644 --- a/test/urlencoded.js +++ b/test/urlencoded.js @@ -19,6 +19,16 @@ describe('bodyParser.urlencoded()', function () { .expect(200, '{"user":"tobi"}', done) }) + it('should parse x-www-form-urlencoded with custom parser', function (done) { + request(createServer({ + parser: function (input) { return input.toUpperCase() } + })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=tobi') + .expect(200, '"USER=TOBI"', done) + }) + it('should 400 when invalid content-length', function (done) { var urlencodedParser = bodyParser.urlencoded() var server = createServer(function (req, res, next) {