qs使用方式+axios
背景
前端传参的时候。一般会用到 axios 或者 fetch 、Ajax 。 fetch 、 axios 慢慢从其中崛起。 qs 实在配合这些 api 时,使用最多的参数格式化工具。
技能列表
qs.parse()将 URL 解析成对象的形式
qs.stringify()将对象序列化成 URL 的形式
传递数组的几种方式
带索引的模式(默认模式)
qs.stringify({ a: ['b', 'c', 'd'] });
// 'a[0]=b&a[1]=c&a[2]=d'
不带索引
qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false });
// 'a=b&a=c&a=d'
arrayFormat 形式格式化
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
// 'a[0]=b&a[1]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
// 'a[]=b&a[]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })
// 'a=b&a=c'
扩展封装(对应后台的接收方式)
- 后端从 Form Data 中获取参数
/**
* 通用查询
* @param url
* @param data
* Form Data 形式
pageNum: 1
pageSize: 20
* @returns {Promise<unknown>}
*/
export const axiosCommonFindPage = ({url, data}) => {
return new Promise((resolve, reject) => {
$axios({
url,
data,
method: 'post',
header: formDataHeader,
transformRequest: [function (data) {
// Do whatever you want to transform the data
return qs.stringify(data, {arrayFormat: 'brackets'});
}],
}).then((res) => {
resolve(res);
}).catch(err => {
reject(err);
})
})
};
- 后端从 Form Data 中获取多层嵌套对象参数
/**
* 通用保存编辑
* @param url
* @param data
* Form Data 形式
* name: 24
* ip: 24
* port: 24
* maxClientNum: 24
* gpuNum: 24
*
*
*
* stringify
* qs.stringify({ a: { b: { c: 'd', e: 'f' } } });
// 'a[b][c]=d&a[b][e]=f'
也可以设置为 dots notation
qs.stringify({ a: { b: { c: 'd', e: 'f' } } }, { allowDots: true });
// 'a.b.c=d&a.b.e=f'
* @returns {Promise<unknown>}
*/
export const axiosCommonSave = ({url, data}) => {
return new Promise((resolve, reject) => {
$axios({
url,
data,
header: formDataHeader,
method: 'post',
transformRequest: [function (data) {
// Do whatever you want to transform the data
return qs.stringify(data,{ allowDots: true })
}],
}).then((res) => {
resolve(res);
}).catch(err => {
reject(err);
})
})
};
- 后端 @RequestParam(“ids”) String[] ids
/**
* 通用删除
* 对应后端接收参数的形式 @RequestParam("ids") String[] ids
* @param url
* @param data
* Form data 形式
* ids: 40288192722a6bb401722ae5f6520007
* ids: 4028819272276694017227674fa30003
* @returns {Promise<unknown>}
*/
export const axiosCommonBatchDelete = ({url, data}) => {
return new Promise((resolve, reject) => {
$axios({
url,
data,
method: 'post',
header: formDataHeader,
transformRequest: [function (data) {
// Do whatever you want to transform the data
return qs.stringify(data,{indices:false});
}],
}).then((res) => {
resolve(res);
}).catch(err => {
reject(err);
})
})
};
- 后端 RequestParam(“ids[]”) String[] ids
/**
* 对应后端接收参数的形式 RequestParam("ids[]") String[] ids
* @param url
* @param data
* Form Data形式
* ids[] 40288192722a980a01722a9ee6420000
* ids[] 40288192722a980a01722a9f198e0001
* @returns {Promise<unknown>}
*/
export const axiosBatchDeleteForArrayBrackets = ({url, data}) => {
return $axios({
url,
method: 'post',
header: formDataHeader,
data,
transformRequest: [function (data) {
// Do whatever you want to transform the data
// brackets 括号的意思
return qs.stringify(data, {arrayFormat: 'brackets'});
}],
})
};
整体代码
import axios from 'axios';
import config, {formDataHeader} from './config';
import Cookies from "js-cookie";
import router from '@/router'
import {Message} from 'element-ui'
import * as login from './moudules/login'
import qs from "qs";
// 使用vuex做全局loading时使用
// import store from '@/store'
export default function $axios(options) {
return new Promise((resolve, reject) => {
let currentOptions = Object.assign({}, config, options);
const instance = axios.create({
baseURL: options.baseUrl ? options.baseUrl : config.baseURL,
headers: options.header ? options.header : config.headers,
timeout: config.timeout,
withCredentials: config.withCredentials,
});
// request 拦截器
instance.interceptors.request.use(
config => {
let token = Cookies.get('token');
// 1. 请求开始的时候可以结合 vuex 开启全屏 loading 动画
// console.log(store.state.loading)
// console.log('准备发送请求...')
// 2. 带上token
if (token) {
config.headers['lx-ticket'] = token;
} else {
// 重定向到登录页面
router.push('/login')
}
// 3. 根据请求方法,序列化传来的参数,根据后端需求是否序列化
if (config.method === 'post') {
// if (config.data.__proto__ === FormData.prototype
// || config.url.endsWith('path')ton
// || config.url.endsWith('mark')
// || config.url.endsWith('patchs')
// ) {
// } else {
// config.data = qs.stringify(config.data)
// }
}
return config
},
error => {
// 请求错误时
console.log('request:', error)
// 1. 判断请求超时
if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
console.log('timeout请求超时')
// return service.request(originalRequest);// 再重复请求一次
}
// 2. 需要重定向到错误页面
const errorInfo = error.response
console.log(errorInfo)
if (errorInfo) {
error = errorInfo.data // 页面那边catch的时候就能拿到详细的错误信息,看最下边的Promise.reject
const errorStatus = errorInfo.status; // 404 403 500 ...
router.push({
path: `/error/${errorStatus}`
})
}
return Promise.reject(error) // 在调用的那边可以拿到(catch)你想返回的错误信息
}
)
// response 拦截器
instance.interceptors.response.use(
response => {
let data;
// IE9时response.data是undefined,因此需要使用response.request.responseText(Stringify后的字符串)
if (response.data == undefined) {
data = JSON.parse(response.request.responseText)
} else {
data = response.data
}
// 根据返回的code值来做不同的处理
switch (data.rc) {
case 1:
console.log(data.desc)
break;
case 0:
store.commit('changeState')
// console.log('登录成功')
default:
}
// 若不是正确的返回code,且已经登录,就抛出错误
// const err = new Error(data.desc)
// err.data = data
// err.response = response
// throw err
if (data && data.responseType && data.msg) {
switch (data.responseType) {
case "success":
Message.success(data.msg);
break;
case "warning":
Message.warning(data.msg);
break;
case "error":
Message.error(data.msg);
break;
default:
Message.info(data.msg);
}
} else {
data.msg && Message.info(data.msg);
}
return data
},
err => {
if (err && err.response) {
switch (err.response.status) {
case 400:
err.message = '请求错误'
break
case 401:
err.message = '未授权,请登录'
break
case 403:
err.message = '拒绝访问'
break
case 404:
err.message = `请求地址出错: ${err.response.config.url}`
break
case 408:
err.message = '请求超时'
break
case 500:
err.message = '服务器内部错误';
if (err.config.url.includes('queryMenus.json')) {
// 如果是查询菜单报错则跳转到登录页签重新登录
sessionStorage.removeItem("user")
Cookies.remove("token");
login.logout().then((res) => {
router.push('/login');
}).catch(function(res) {
router.push('/login');
})
}
break;
case 501:
err.message = '服务未实现'
break
case 502:
err.message = '网关错误'
break
case 503:
err.message = '服务不可用'
break
case 504:
err.message = '网关超时'
break
case 505:
err.message = 'HTTP版本不受支持'
break
default:
}
}
console.error(err)
return Promise.reject(err) // 返回接口返回的错误信息
}
)
// 请求处理
instance(options).then(res => {
resolve(res)
return false
}).catch(error => {
reject(error)
})
})
}
/**
* 通用保存编辑
* @param url
* @param data
* Form Data 形式
* name: 24
* ip: 24
* port: 24
* maxClientNum: 24
* gpuNum: 24
*
*
*
* stringify
* qs.stringify({ a: { b: { c: 'd', e: 'f' } } });
// 'a[b][c]=d&a[b][e]=f'
也可以设置为 dots notation
qs.stringify({ a: { b: { c: 'd', e: 'f' } } }, { allowDots: true });
// 'a.b.c=d&a.b.e=f'
* @returns {Promise<unknown>}
*/
export const axiosCommonSave = ({url, data}) => {
return new Promise((resolve, reject) => {
$axios({
url,
data,
header: formDataHeader,
method: 'post',
transformRequest: [function (data) {
// Do whatever you want to transform the data
return qs.stringify(data,{ allowDots: true })
}],
}).then((res) => {
resolve(res);
}).catch(err => {
reject(err);
})
})
};
/**
* 通用查询
* @param url
* @param data
* Form Data 形式
pageNum: 1
pageSize: 20
* @returns {Promise<unknown>}
*/
export const axiosCommonFindPage = ({url, data}) => {
return new Promise((resolve, reject) => {
$axios({
url,
data,
method: 'post',
header: formDataHeader,
transformRequest: [function (data) {
// Do whatever you want to transform the data
return qs.stringify(data, {arrayFormat: 'brackets'});
}],
}).then((res) => {
resolve(res);
}).catch(err => {
reject(err);
})
})
};
/**
* 通用删除
* 对应后端接收参数的形式 @RequestParam("ids") String[] ids
* @param url
* @param data
* Form data 形式
* ids: 40288192722a6bb401722ae5f6520007
* ids: 4028819272276694017227674fa30003
* @returns {Promise<unknown>}
*/
export const axiosCommonBatchDelete = ({url, data}) => {
return new Promise((resolve, reject) => {
$axios({
url,
data,
method: 'post',
header: formDataHeader,
transformRequest: [function (data) {
// Do whatever you want to transform the data
return qs.stringify(data,{indices:false});
}],
}).then((res) => {
resolve(res);
}).catch(err => {
reject(err);
})
})
};
/**
* 对应后端接收参数的形式 RequestParam("ids[]") String[] ids
* @param url
* @param data
* Form Data形式
* ids[] 40288192722a980a01722a9ee6420000
* ids[] 40288192722a980a01722a9f198e0001
* @returns {Promise<unknown>}
*/
export const axiosBatchDeleteForArrayBrackets = ({url, data}) => {
return $axios({
url,
method: 'post',
header: formDataHeader,
data,
transformRequest: [function (data) {
// Do whatever you want to transform the data
// brackets 括号的意思
return qs.stringify(data, {arrayFormat: 'brackets'});
}],
})
};
uni-app + qs
近些年崛起的比较快。在使用的时候,也是需要用到数据转换的点。故贴几个实际使用的图,在了解上面用法的基础上,再次使用 qs 转换参数比较简单
安装 qs
# 使用命令安装后
npm i qs --save
import $H from '@/common/lib/request.js';
import qs from 'qs'
export default class ApiCommon {
constructor(requestMapping) {
this._requestMapping = requestMapping || '';
this.findPage = this.findPage.bind(this);
this.save = this.save.bind(this);
this.batchDelete = this.batchDelete.bind(this);
}
findPage(data) {
return $H.post(this._requestMapping + '/list.json', qs.stringify(data,{ allowDots: true }))
}
save(data) {
return $H.post(this._requestMapping + '/save.json', qs.stringify(data,{ allowDots: true,indices:false}))
}
batchDelete(data) {
return $H.post(this._requestMapping + '/delete.json', qs.stringify(data,{indices:false}));
}
}
基础公共封装
import $store from '@/store/index.js'
import qs from 'qs'
export default {
// 全局配置
common: {
// #ifndef H5
baseUrl: "http://124.70.132.32:8080",
// #endif
// #ifdef H5
baseUrl: "",
// #endif
header: {
payload: {
'Content-Type': 'application/json;charset=UTF-8',
},
formData: {
'Content-Type': 'application/x-www-form-urlencoded',
}
},
data: {},
method: 'GET',
dataType: 'json'
},
// 请求 返回promise
request(options = {}) {
// 组织参数
options.url = this.common.baseUrl + options.url
options.header = options.header || this.common.header.formData
options.data = options.data || this.common.data
options.method = options.method || this.common.method
options.dataType = options.dataType || this.common.dataType
// token
if (options.token) {
options.header.token = $store.state.user.token
// 二次验证
if (options.checkToken && !options.header.token) {
uni.showToast({
title: '请先登录',
icon: 'none'
});
return uni.navigateTo({
url: '/pages/login/login',
});
}
}
// 请求
return new Promise((res, rej) => {
// 请求之前... todo
// 请求中...
uni.request({
...options,
success: (result) => {
// 返回原始数据
if (options.native) {
return res(result)
}
// 服务端失败
// if (result.statusCode !== 200) {
// if (options.toast !== false) {
// uni.showToast({
// title: result.data.msg || '服务端失败',
// icon: 'none'
// });
// }
// return rej(result.data)
// }
// 成功
let data = result.data
res(data)
},
fail: (error) => {
uni.showToast({
title: error.errMsg || '请求失败',
icon: 'none'
});
return rej()
}
});
})
},
// get请求
get(url, data = {}, options = {}) {
options.url = url
options.data = data
options.method = 'GET'
return this.request(options)
},
// post请求
post(url, data = {}, options = {}) {
options.url = url
options.data = data
options.method = 'POST'
return this.request(options)
},
// delete请求
del(url, data = {}, options = {}) {
options.url = url
options.data = data
options.method = 'DELETE'
return this.request(options)
},
postSave(url, data = {}, options = {}) {
options.url = url
options.data = qs.stringify(data, {
allowDots: true,
indices: false
})
options.method = 'POST'
return this.request(options)
},
postFind(url, data = {}, options = {}) {
options.url = url
options.data = qs.stringify(data, {
allowDots: true
})
options.method = 'POST'
return this.request(options)
},
postBatchDelete(url, data = {}, options = {}) {
options.url = url
options.data = qs.stringify(data, {
indices: false
})
options.method = 'POST'
return this.request(options)
}
}