Add configurable query parser

closes #2215
This commit is contained in:
Douglas Christopher Wilson 2014-07-13 23:15:00 -04:00
parent b43205ca98
commit e29fa25bb4
5 changed files with 149 additions and 41 deletions

View File

@ -1,6 +1,11 @@
unreleased
==========
* configurable query parser with `app.set('query parser', parser)`
- `app.set('query parser', 'extended')` parse with "qs" module
- `app.set('query parser', 'simple')` parse with "querystring" core module
- `app.set('query parser', false)` disable query string parsing
- `app.set('query parser', true)` enable simple parsing
* perf: prevent multiple `Buffer` creation in `res.send`
4.6.1 / 2014-07-12

View File

@ -12,6 +12,7 @@ var debug = require('debug')('express:application');
var View = require('./view');
var http = require('http');
var compileETag = require('./utils').compileETag;
var compileQueryParser = require('./utils').compileQueryParser;
var compileTrust = require('./utils').compileTrust;
var deprecate = require('depd')('express');
var resolve = require('path').resolve;
@ -51,6 +52,7 @@ app.defaultConfiguration = function(){
this.set('etag', 'weak');
var env = process.env.NODE_ENV || 'development';
this.set('env', env);
this.set('query parser', 'extended');
this.set('subdomain offset', 2);
this.set('trust proxy', false);
@ -104,7 +106,7 @@ app.lazyrouter = function() {
strict: this.enabled('strict routing')
});
this._router.use(query());
this._router.use(query(this.get('query parser fn')));
this._router.use(middleware.init(this));
}
};
@ -306,6 +308,10 @@ app.set = function(setting, val){
debug('compile etag %s', val);
this.set('etag fn', compileETag(val));
break;
case 'query parser':
debug('compile query parser %s', val);
this.set('query parser fn', compileQueryParser(val));
break;
case 'trust proxy':
debug('compile trust proxy %s', val);
this.set('trust proxy fn', compileTrust(val));

View File

@ -2,36 +2,27 @@
* Module dependencies.
*/
var qs = require('qs');
var parseUrl = require('parseurl');
var qs = require('qs');
/**
* Query:
*
* Automatically parse the query-string when available,
* populating the `req.query` object using
* [qs](https://github.com/visionmedia/node-querystring).
*
* Examples:
*
* .use(connect.query())
* .use(function(req, res){
* res.end(JSON.stringify(req.query));
* });
*
* The `options` passed are provided to qs.parse function.
*
* @param {Object} options
* @return {Function}
* @api public
*/
module.exports = function query(options){
module.exports = function query(options) {
var queryparse = qs.parse;
if (typeof options === 'function') {
queryparse = options;
options = undefined;
}
return function query(req, res, next){
if (!req.query) {
req.query = ~req.url.indexOf('?')
? qs.parse(parseUrl(req).query, options)
: {};
var val = parseUrl(req).query;
req.query = queryparse(val, options);
}
next();

View File

@ -7,6 +7,8 @@ var crc32 = require('buffer-crc32');
var crypto = require('crypto');
var basename = require('path').basename;
var proxyaddr = require('proxy-addr');
var qs = require('qs');
var querystring = require('querystring');
var typer = require('media-typer');
/**
@ -202,6 +204,41 @@ exports.compileETag = function(val) {
return fn;
}
/**
* Compile "query parser" value to function.
*
* @param {String|Function} val
* @return {Function}
* @api private
*/
exports.compileQueryParser = function compileQueryParser(val) {
var fn;
if (typeof val === 'function') {
return val;
}
switch (val) {
case true:
fn = querystring.parse;
break;
case false:
fn = newObject;
break;
case 'extended':
fn = qs.parse;
break;
case 'simple':
fn = querystring.parse;
break;
default:
throw new TypeError('unknown value for query parser function: ' + val);
}
return fn;
}
/**
* Compile "proxy trust" value to function.
*
@ -252,3 +289,14 @@ exports.setCharset = function(type, charset){
// format type
return typer.format(parsed);
};
/**
* Return new empty objet.
*
* @return {Object}
* @api private
*/
function newObject() {
return {};
}

View File

@ -5,33 +5,91 @@ var express = require('../')
describe('req', function(){
describe('.query', function(){
it('should default to {}', function(done){
var app = express();
app.use(function(req, res){
req.query.should.eql({});
res.end();
});
var app = createApp();
request(app)
.get('/')
.end(function(res){
done();
});
})
.expect(200, '{}', done);
});
it('should contain the parsed query-string', function(done){
var app = express();
app.use(function(req, res){
req.query.should.eql({ user: { name: 'tj' }});
res.end();
});
it('should default to parse complex keys', function (done) {
var app = createApp();
request(app)
.get('/?user[name]=tj')
.end(function(res){
done();
.expect(200, '{"user":{"name":"tj"}}', done);
});
describe('when "query parser" is extended', function () {
it('should parse complex keys', function (done) {
var app = createApp('extended');
request(app)
.get('/?user[name]=tj')
.expect(200, '{"user":{"name":"tj"}}', done);
});
})
});
describe('when "query parser" is simple', function () {
it('should not parse complex keys', function (done) {
var app = createApp('simple');
request(app)
.get('/?user[name]=tj')
.expect(200, '{"user[name]":"tj"}', done);
});
});
describe('when "query parser" is a function', function () {
it('should parse using function', function (done) {
var app = createApp(function (str) {
return {'length': (str || '').length};
});
request(app)
.get('/?user[name]=tj')
.expect(200, '{"length":17}', done);
});
});
describe('when "query parser" disabled', function () {
it('should not parse query', function (done) {
var app = createApp(false);
request(app)
.get('/?user[name]=tj')
.expect(200, '{}', done);
});
});
describe('when "query parser" disabled', function () {
it('should not parse complex keys', function (done) {
var app = createApp(true);
request(app)
.get('/?user[name]=tj')
.expect(200, '{"user[name]":"tj"}', done);
});
});
describe('when "query parser" an unknown value', function () {
it('should throw', function () {
createApp.bind(null, 'bogus').should.throw(/unknown value.*query parser/);
});
});
})
})
function createApp(setting) {
var app = express();
if (setting !== undefined) {
app.set('query parser', setting);
}
app.use(function (req, res) {
res.send(req.query);
});
return app;
}