vue+axios x-www-form-urlencoded参
前言
在使用vue-resource的时候,想要使post传参为x-www-form-urlencoded类型的content-type,只需要增加一行配置Vue.http.options.emulateJSON = true就可以解决问题。但是到了axios就没有那么简单了。进过分析、尝试。最终解决了问题。以下是我的解决方案。网上有很多解决方案。我没有采纳,也就没有去尝试。
网上的方法
网上找到的解决方案基本差不多。用URLSearchParams,却说兼容性不好,而且要改请求的代码,这样太严重了,要改太多接口。新项目还可以试,老项目已经一大堆的请求。去一个一个改很难接受。还有些方案描述的不清楚,貌似适合他所在的项目中。或者有环境依赖的。不一一去说了。找了很久没有觉得符合我想要的结果。
源码分析
经过查看源码,发现以下一段代码:
var defaults = {
adapter: getDefaultAdapter(),
transformRequest: [function transformRequest(data, headers) {
normalizeHeaderName(headers, 'Content-Type');
if (utils.isFormData(data) ||
utils.isArrayBuffer(data) ||
utils.isBuffer(data) ||
utils.isStream(data) ||
utils.isFile(data) ||
utils.isBlob(data)
) {
return data;
}
if (utils.isArrayBufferView(data)) {
return data.buffer;
}
if (utils.isURLSearchParams(data)) {
setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
return data.toString();
}
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data);
}
return data;
}],
}
...
看得出,不段代码中如果传的参数是URLSearchParams对象的,使用x-www-form-urlencoded的Content-Type。所以网上说的那种方案是可行的。
其实axios默认的content-type是application/x-www-form-urlencoded;charset=utf-8。只是参数对象在其它判断都不通过,isObject的时候才会是json。
这是axios默认配置中的一项,transformRequest这是一个数组。我就想,如果数组里有多个项的话会是怎么执行呢。
找到如下代码:
module.exports = function transformData(data, headers, fns) {
/*eslint no-param-reassign:0*/
utils.forEach(fns, function transform(fn) {
data = fn(data, headers);
});
return data;
};
原来如果transformRequest数组中有多个转换器的时候,它会挨个执行,实现了转换器链。
解决问题
经过源码分析,要解决这个访问就简单了。向axios的全局配置中加入一个转换器。我觉得应该加默认转换器前面。就不需要设置Content-Type了。
const customTransformData = (data, headers) => {
if (utils.isFormData(data) ||
utils.isArrayBuffer(data) ||
utils.isBuffer(data) ||
utils.isStream(data) ||
utils.isFile(data) ||
utils.isBlob(data) ||
utils.isArrayBufferView(data) ||
utils.isURLSearchParams(data)
) {
return data
}
if (utils.isObject(data)) {
for (var pkey in data) {
if (data[pkey] === null || typeof (data[pkey]) === 'undefined') {
delete data[pkey]
}
}
data = utils.params(data)
return data
}
return data
}
// 加入到数据最前面
axios.defaults.transformRequest.unshift(customTransformData)
为了不影响其它转换器的执行。我只是把isObject的做了处理。
其中:
utils:复制了axios中的utils的相应函数。
utils.params:这是参考了vue-resource中参数转换的代码,加入到utils中的。
以下是utils的代码,供参考
import _ from 'lodash'
var isBuffer = _.isBuffer
/* global toString:true */
// utils is a library of generic helper functions non-specific to axios
var toString = Object.prototype.toString
/**
* Determine if a value is an ArrayBuffer
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an ArrayBuffer, otherwise false
*/
function isArrayBuffer (val) {
return toString.call(val) === '[object ArrayBuffer]'
}
/**
* Determine if a value is a FormData
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an FormData, otherwise false
*/
function isFormData (val) {
return (typeof FormData !== 'undefined') && (val instanceof FormData)
}
/**
* Determine if a value is a view on an ArrayBuffer
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false
*/
function isArrayBufferView (val) {
var result
if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
result = ArrayBuffer.isView(val)
} else {
result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer)
}
return result
}
/**
* Determine if a value is an Object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Object, otherwise false
*/
function isObject (val) {
return val !== null && typeof val === 'object'
}
/**
* Determine if a value is a File
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a File, otherwise false
*/
function isFile (val) {
return toString.call(val) === '[object File]'
}
/**
* Determine if a value is a Blob
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Blob, otherwise false
*/
function isBlob (val) {
return toString.call(val) === '[object Blob]'
}
/**
* Determine if a value is a Function
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Function, otherwise false
*/
function isFunction (val) {
return toString.call(val) === '[object Function]'
}
/**
* Determine if a value is a Stream
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Stream, otherwise false
*/
function isStream (val) {
return isObject(val) && isFunction(val.pipe)
}
/**
* Determine if a value is a URLSearchParams object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a URLSearchParams object, otherwise false
*/
function isURLSearchParams (val) {
return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
}
/**
* Encodes a Url parameter string.
*
* @param {Object} obj
*/
function params (obj) {
let params = []
let escape = encodeURIComponent
params.add = function (key, value) {
if (_.isFunction(value)) {
value = value()
}
if (value === null) {
value = ''
}
this.push(escape(key) + '=' + escape(value))
}
serialize(params, obj)
return params.join('&').replace(/%20/g, '+')
}
function serialize (params, obj, scope) {
let array = _.isArray(obj)
let plain = _.isPlainObject(obj)
let hash = null
_.forEach(obj, (value, key) => {
hash = _.isObject(value) || _.isArray(value)
if (scope) {
key = scope + '[' + (plain || hash ? key : '') + ']'
}
if (!scope && array) {
params.add(value.name, value.value)
} else if (hash) {
serialize(params, value, key)
} else {
params.add(key, value)
}
})
}
export default {
isArrayBuffer: isArrayBuffer,
isBuffer: isBuffer,
isFormData: isFormData,
isArrayBufferView: isArrayBufferView,
isObject: isObject,
isFile: isFile,
isBlob: isBlob,
isStream: isStream,
isURLSearchParams: isURLSearchParams,
params: params
}