参考文档:axios
参考博客:《vue中Axios的封装和API接口的管理》《Axios源码深度剖析》
axios的封装和api接口的统一管理,其实主要目的就是在帮助我们简化代码和利于后期的更新维护
配置axios
// 设置请求跟路径
axios.defaults.baseURL = 'https://www.abc.com/project/api/'
// 设置请求超时时间,如果超过时间在响应拦截阶段提示用户
axios.defaults.timeout = 10000
// post请求headers中设置Content-Type,如果后端设置的接收类型是 application/x-www-form-urlencoded,则前端发送时需保持一致
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
请求拦截
我们在发送请求前可以进行一个请求的拦截。比如,有些请求是需要用户登录之后才能访问的。
token使用流程
在登录完成之后,将用户的token通过localStorage或者cookie存在本地,之后用户每次在进入页面的时候,会首先从本地存储中读取token,如果token存在说明用户已经登录过,则更新vuex中的token状态,在每次请求接口的时候,都会在请求的header中携带token,后台人员就可以根据你携带的token来判断你的登录是否过期。如果没有携带,则说明没有登录过。
这时候或许有些小伙伴会有疑问了,就是每个请求都携带token,那么要是一个页面不需要用户登录就可以访问的怎么办呢?
其实,你前端的请求可以携带token,但是后台可以选择不接收啊!
App.vue
import store from '@/store'
export default {
name: "App",
created() {
// 获取本地存储的token
const token = localStorage.getItem('token')
token && store.commit('setToken', { token })
}
}
请求拦截
import store from '@/store'
axios.interceptors.request.use(
config => {
// 每次发送请求之前判断vuex中是否存在token
// 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
const token = store.state.token
token && (config.headers.Authorization = token)
return config
},
error => {
return Promise.error(error)
}
)
响应拦截
响应拦截主要处理响应失败的情况(响应成功直接返回),例如token过期,没有权限等,可以先与后端人员协商好错误码,然后在拦截过程中根据错误码引导用户不同的操作,如重新登录
import router from '@/router'
import store from '@/store'
axios.interceptors.response.use(
response => {
// 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据。否则的话抛出错误
if (response.status === 200) {
return Promise.resolve(response)
} else {
return Promise.reject(response)
}
},
// 服务器状态码不是2开头的的情况
// 下面列举几个常见的操作,其他需求可自行扩展
error => {
if (error.response.status) {
switch (error.response.status) {
// 401: 未登录
// 未登录则跳转登录页面,并携带当前页面的路径
// 在登录成功后返回当前页面,这一步需要在登录页操作。
case 401:
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
})
break
// 403 token过期
// 登录过期对用户进行提示
// 清除本地token和清空vuex中token对象
// 跳转登录页面
case 403:
Toast('登录过期,请重新登录')
localStorage.removeItem('token')
store.commit('clearToken')
// 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
setTimeout(() => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
})
}, 1000)
break
}
return Promise.reject(error.response)
}
}
})
GET请求封装
export function get(url, params) {
return new Promise((resolve, reject) => {
axios.get(url, { params: params })
.then(res => {
resolve(res.data)
})
.catch(err => {
reject(err.data)
})
})
}
POST请求封装
axios.get()方法和axios.post()在提交数据时参数的书写方式还是有区别的。get的第二个参数是一个{},然后这个对象的params属性值是需要传递的参数对象;post的第二个参数就是需要传递的参数对象
如果后端设定的Centent-Type为application/x-www-form-urlencoded,则post请求前需要对参数对象(通常是json形式)进行序列化。如 { a:1, b: 2 },序列化后为 a=1&b=2
import qs from 'qs'
export function post(url, params) {
return new Promise((resolve, reject) => {
axios.post(url, qs.stringify(params)) // 参数序列化
.then(res => {
resolve(res.data)
})
.catch(err => {
reject(err.data)
})
})
}
在项目中建立request文件夹,并建立http.js与api.js
http.js
import axios from 'axios'
import qs from 'qs'
import store from '@/store'
import router from '@/router'
import { Toast, MessageBox } from 'mint-ui'
// api根路径
axios.defaults.baseURL = 'https://www.abc.com/api/'
// 请求超时时间,超时可在响应拦截中处理
axios.defaults.timeout = 10000
// 后端设置参数接收类型为application/x-www-form-urlencoded,这里也需要设置post请求Content-Type类型
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
axios.interceptors.request.use(
config => {
// Authorization为后端规定请求头部携带验证身份的属性名(通常为token)
const token = store.state.token
token && (config.headers.Authorization = token)
return config
},
error => {
return Promise.error(error)
}
)
axios.interceptors.response.use(
response => {
if (response.status === 200) {
return Promise.resolve(response)
} else {
return Promise.reject(response)
}
},
error => {
// 请求超时的错误信息
if (error.message.includes('timeout')) {
MessageBox('提示', '请求超时,请检查网络')
}
// 根据错误码进行相应提示、操作、路由跳转
if (error.response.status) {
switch (error.response.status) {
case 401:
console.log('401')
break
}
return Promise.reject(error.response)
}
}
)
export function get(url, params) {
return new Promise((resolve, reject) => {
axios.get(url, { params: params })
.then(res => {
resolve(res.data)
})
.catch(err => {
reject(err.data)
})
})
}
export function post(url, params) {
return new Promise((resolve, reject) => {
axios.post(url, qs.stringify(params))
.then(res => {
resolve(res.data)
})
.catch(err => {
reject(err.data)
})
})
}
api,js
import { get, post } from './http'
// 店铺列表
const barberList = p => post('v2.Store/StoreList', p)
export {
barberList
}
home.vue
import { barberList } from "@/request/api"
import { Toast } from 'mint-ui'
const ERR_OK = 1
export default {
name: "home",
data() {
return {
baberList: [], // 店铺列表
}
},
created() {
this.getBarberList()
},
methods: {
getBarberList() {
barberList().then(res => {
if (res.code === ERR_OK) {
this.baberList = res.data.data
}
else {
Toast(res.msg)
}
})
}
}
}