封装一个 axios url encoding serialize util
该 util 遵从的是 Json API,即 swagger, 这一规范,可以将对应的 query 进行 encoding。
主自用,使用 TS,参考对象为 lodash-contrib。
axios 默认传递到后台的数据为 json 格式,官方在文档:URL-Encoding Bodies 中建议在浏览器环境下可以直接使用 URLSearchParams 或是 qs,使用方法如下:
-
const params = new URLSearchParams(); params.append('param1', 'value1'); params.append('param2', 'value2'); axios.post('/foo', params);
-
const qs = require('qs'); axios.post('/foo', qs.stringify({ bar: 123 }));
ES6:
import qs from 'qs'; const data = { bar: 123 }; const options = { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, data: qs.stringify(data), url, }; axios(options);
我们的 JSON 规范遵从的是这一条:Json API,直接使用 URLSearchParams
好像是行不通的,因为有一些 filter 会使用 [
和 ]
,如:/author/1/book?filter[book]=genre=='Science Fiction';title==The*;price.total>100.00
,之前直接使用 URLSearchParams
似乎就报错了。
当然,也有可能是因为 encoding 没有完全转换的关系。
出于同样的原因,直接使用 qs
也存在一定的问题,似乎可能是因为需要重新修改 encoder、queryPrefix 等因素,总之没有能够在有限的时间内完成调整。
最终还是稍微修改了一下 lodash-contrib 中的 toQuery
,具体实现如下:
import _ from 'lodash';
// some of the util functions are from lodash-contrib but in TS form, credit: https://github.com/node4good/lodash-contrib
// recreated here due to lodash-contrib does not have TS support and cannot be used in the project.
type validQuery = string | number | boolean;
type validQueryObj =
| { [key: string]: validQuery | validQueryObj }
| validQuery[]
| validQuery;
const buildParams = (
prefix: string | number,
val: validQueryObj,
top = true
): string => {
if (_.isUndefined(top)) top = true;
if (_.isArray(val)) {
return _.map(val, function (value, key) {
return buildParams(top ? key : prefix + '[]', value, false);
}).join('&');
} else if (_.isObject(val)) {
return _.map(val, function (value, key) {
return buildParams(top ? key : prefix + '[' + key + ']', value, false);
}).join('&');
} else {
return encodeURIComponent(prefix) + '=' + encodeURIComponent(val);
}
};
export const toQuery = function (obj: validQueryObj): string {
return buildParams('', obj);
};
使用方式如下:
export const fetch = async ({
apiMethod,
uri,
params,
}: FetchProps): Promise<AxiosResponse<any, any>> => {
return await axiosInstance({
method: apiMethod,
url: uri,
params,
paramsSerializer: (params) => {
return toQuery(params);
},
});
};
type guard 目前不是最好的实现,还是需要根据 generics 进行对应的修改,不过这应该算是一个比较通用的解决方案了。