前言
在工作中新接手了一个项目,前端后台都是刚接手,代码都未细看就开始开发了,在前后端约定传递参数格式时,后端希望前端传递的格式中有一个参数是数组。如下:
const params = {
name: 'xxx',
lists: [ 'aaa', 'bbb']
}
于是我们两就哼哧哼哧的写完,结果一联调,后端发现接收到的数组数据格式不符合预期,去查看代码发现是因为在项目中,axios封装中,数据请求格式为application/x-www-form-urlencoded
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded'
同时也引入了qs统一做序列化的处理
import qs from 'qs'
qs.stringify(params)
经过序列化后的数组格式类似是:
lists[0]='aaa'&list[1]='bbb'
现项目的后端没有写相关的接收数据处理,所以我们就细看代码,发现之前的接口,对于数组都是转成字符串的形式传递
const params = {
name: 'xxx',
lists: 'aaa,bbb'
}
于是我们就愉快的决定这样传递快速的解决问题。
虽然项目中解决了问题,需求完成了,但是对于这一块的知识我是完全不清楚,于是去查资料且写下此文章记录一下。
axios发送请求的数据格式
Content-Type
Content-Type编码类型,是指http、https发送信息至服务器时的内容编码类型,用于表明发送数据流的类型,服务器根据编码类型使用特定的解析方式,获取数据流中的数据。
常用的Content-Type有如下:
text/html,text/plain,text/css,text/javascript,image/jpeg,image/png,image/gif等常见的页面资源类型。
application/x-www-form-urlencoded,multipart/form-data,application/json,application/xml,这四个是表单提交或上传文件的常用的资源类型。
axios的常见的数据格式(content-type)
- application/json: 请求体中的数据会以json字符串的形式发送到后端
- application/x-www-form-urlencoded:请求体中的数据会以普通表单形式(键值对)发送到后端
- multipart/form-data: 它会将请求体的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。
axios默认的请求头content-type类型是application/json
说明:由于公司电脑不能截图,请求在浏览器Network上的截图借用的网上的图(ps:希望以后自己可以模拟出来),借用的图的来源链接会在文中说明。
application/json
如果前后端约定的入参是json,那么不需要做任何处理。
json格式的get请求的编码方式是:Query String Parameters
json格式的get请求的编码方式是:Request Payload
application/x-www-form-urlencoded
如果前后端约定的入参格式是application/x-www-form-urlencoded ,则编码方式是编码方式formdata( 表单格式数据提交),用这种方式要把json {name:lisi,age:4}格式转换为name=lisi&age=4的格式
所以数据处理方法如下:
一:利用qs将参数转换为query参数
引入:
import axios from 'axios'
import qs from 'qs'
使用示例:
let data = {
name: 'lisi',
age: '4'
}
axios({
headers: {'Content-Type':'application/x-www-form-urlencoded'},
method: 'post',
url: url,
data: Qs.stringify(data)
})
qs的说明:
1.将url中的参数转为对象;2.将对象转为url参数形式。
qs处理数组
qs.stringify({ids: [1, 2, 3]}, { indices: false })
// ids=1&ids=2&ids=3
qs.stringify({ids: [1, 2, 3]}, { indices: true })
// ids[0]=1&ids[1]=2&id[2]=3
qs.stringify({ids: [1, 2, 3]}, {arrayFormat: ‘repeat‘})
// ids=1&ids=2&ids=3
qs.stringify({ids: [1, 2, 3]}, {arrayFormat: ‘brackets‘})
// ids[]=1&ids[]=2&ids[]=3
qs.stringify({ids: [1, 2, 3]}, {arrayFormat: ‘indices‘})
// ids[0]=1&ids[1]=2&ids[2]=3
qs.stringify({ids: [1, 2, 3]}, {arrayFormat: ‘comma‘})
// ids[0]=1&ids[1]=2&ids[2]=3
二:用 URLSearchParams 传递参数
let param = new URLSearchParams()
param.append('name', 'lisi')
param.append('age', 4)
axios({
method: 'post',
url: url,
data: param
})
当传递的是数组的时候,没有做处理之前,会呈现为 list:[object object],[object object]。
将数组转换成JSON字符串可以解决此问题:
let param = new URLSearchParams()
param.append('name', 'lisi')
param.append('age', 4)
param.append('list', JSON.stringify([{name: 'lisi', age: 4}]))
axios({
method: 'post',
url: url,
data: param
})
PS:qs.stringify()和JSON.stringify的区别:
{name: 'lisi',age: 4}
qs.stringify()转化为name="lisi"&age=4
JSON.stringify转化为{"name": "lisi","age":"4"}
multipart/form-data
表单数据中,包含有上传图片、上传视频音频等。此类上传表单,只能是Content-Type:multipart/form-data,以Form Data编码方式传送数据。
参考网站:
axios 发送数据请求的方式
axios的二次封装
在vue项目中,与后台交互获取数据,一般采用的是axios库。
什么是axios?
Axios是一个基于promise用于浏览器和node.js的简单HTTP客户端。Axios在一个具有可扩展接口的很小体积的JS包中提供了一个易于使用的库。
特点:
- 从浏览器中创建 XMLHttpRequest
- 从 node.js 发出 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防止 CSRF/XSRF
安装:
npm install axios;
axios.ts
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"
interface Result {
code: number;
msg: string;
}
// T = any 泛型参数默认类型
interface ResultData<T = any> extends Result{
data?: T
}
const URL:string = '/mock'
// 状态码一般是前后端约定
enum RequestEnums {
TIMEOUT = 20000, // 超时
OVERDUE = 600, // 过期
FAIL = 999, // 失败
SUCCESS = 200 // 成功
}
const config = {
baseURL: URL as string,
timeout: RequestEnums.TIMEOUT as number,
// 跨域时允许携带凭证
withCredentials: true
}
class RequestHttp {
protected instance: AxiosInstance;
public constructor(config: AxiosRequestConfig){
this.instance = axios.create(config)
this.initRequestInterceptor()
this.initResponseInterceptor()
}
/**
* 请求拦截器
* 客户端发送请求--》请求拦截器--》服务器
* token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
*/
private initRequestInterceptor = () => {
this.instance.interceptors.request.use(this.handleRequest)
}
private handleRequest = (config: AxiosRequestConfig) => {
// 存储在本地
const token = localStorage.getItem('token') || '';
return {
...config,
headers: {
'Authorization': token
}
}
}
/**
* 响应拦截器
* 服务器返回信息 --》拦截,统一处理 --》客户端js获取信息
*/
private initResponseInterceptor = () => {
this.instance.interceptors.response.use((response: AxiosResponse) => {
// 解构
const {data, config} = response
if(data.code && data.code === RequestEnums.OVERDUE){
// 登录信息失效,应跳转到登录页面,并清空本地的token
localStorage.setItem('token', '');
// router.replace({
// path: '/login'
// })
return Promise.reject(data);
}
if(data.code && data.code !== RequestEnums.SUCCESS){
// 返回错误
return Promise.reject(data)
}
return data
},(error: AxiosError) => {
const { response } = error
if(response){
this.handleCode(response.status)
}
// 处理404
})
}
private handleCode(code: number): void {
switch(code){
case 401:
console.log('登陆失败,请重新登录')
break
default:
console.log('请求失败')
break
}
}
// 常用方法封装
get<T>(url: string, params?: object): Promise<ResultData<T>>{
return this.instance.get(url, params)
}
post<T>(url: string, params?: object): Promise<ResultData<T>>{
return this.instance.post(url, params)
}
put<T>(url: string, params?: object): Promise<ResultData<T>>{
return this.instance.put(url, params)
}
delete<T>(url: string, params?: object): Promise<ResultData<T>>{
return this.instance.delete(url, params)
}
}
// 导出一个实例对象
export default new RequestHttp(config)
api->login.ts
import axios from "../plugins/axios";
namespace Login{
// 用户登陆表单
export interface LoginReqForm{
username: string;
password: string;
}
export interface LoginResData{
token: string;
}
}
// 用户登录
export const login = (params: Login.LoginReqForm) => {
return axios.post<Login.LoginResData>('/user/login', params)
}
页面应用
<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue'
import { login } from './../api/login'
export default defineComponent({
name: 'Cart',
components: {
},
setup(){
const loginForm = {
username: 'test',
password: 'test'
}
const loginFun = async () => {
const data = await login(loginForm)
console.log('data', data)
}
return { loginFun }
}
})
</script>
<template>
<div>
cart页面
<router-link class="btn btn-primary btn-lg btn-block" to="/checkout">Continue to checkout</router-link>
<button @click="loginFun">测试接口</button>
</div>
</template>