Express查询字符串数组解析为对象的问题

这几天检查日志,每天总会有大量上游的调用请求出现400错误(Expected type array but found type object), 这个API Get请求参数如下:

1
?arr[]=value1&arr[]=value2&arr[]=value3...

进一步分析日志发现,上游的查询参数中数组是有指定索引的,比如

1
?arr[0]=value1&arr[1]=value2&arr[2]=value3...

而且奇怪的是并非所有的请求都有问题,只有当arr[]超过20个才会出现,第一反应是Express的query parser问题,将查询字符串数组解析为对象,

原来Express已经不包含大部分的请求解析中间件了,如json、urlencoded、cookie等中间件都变成可配置的了,只有查询字符串解析中间件还是内置的,其中解析函数默认为qs模块的,可以通过app上的query parse设置。

qs可以根据[]来解析数组,也支持指定索引。

1
2
qs.parse('a[]=b&a[]=c'); // { a: ['b', 'c'] }
qs.parse('a[1]=c&a[0]=b'); // { a: ['b', 'c'] }

查询qs的文档发现:

qs will also limit specifying indices in an array to a maximum index of 20. Any array members with an index of greater than 20 will instead be converted to an object with the index as the key. This is needed to handle cases when someone sent, for example, a[999999999] and it will take significant time to iterate over this huge array.

qs默认数组长度只到20, 所以当指定的索引超过20时,整个属性会被当作对象处理

1
qs.parse('a[100]=b'); // { a: { '100': 'b' } }

可以通过修改arrayLimit 来指定允许解析的数组的最大索引。

1
2
qs.parse('a[1]=b&a[2]=1', { arrayLimit: 0 }) // { a: { 1: 'b', 2: 1 } }
qs.parse('a[1]=b&a[2]=1', { arrayLimit: 2 }) // { a: ['b', 1] }

解决方法:修改Express的的query parse设置,代码如下

1
2
3
4
5
6
7
const express = require('express');
const qs = require('qs');
const app = express();

app.set('query parser', function (str) {
return qs.parse(str, opts);
});

参考连接