axios的封装和api接口的统一管理
1. 什么是 axios
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
特性
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
2. 拦截器
axios 请求拦截器 (一般一个回调)
是什么:在真正发请求之前执行的一个回调函数。
作用: 对所有的请求做统一的处理,如追加请求头 、追加参数、界面loading提示等。
axios 响应拦截器
是什么:得到响应之后执行的一组回调函数
作用: 若请求成功,对成功的数据进行处理 ;若请求失败,对失败进行统一的操作。
注意:可以指定多个请求拦截器,先指定的先执行
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
3. 为什么要封装 axios
axios 本身就是封装好了的请求库,为什么我们要对其进行二次封装呢?这个原因很简单,和大多数的封装一样,目的是为了简化代码使用以及便于后期的更新与维护。
axios的封装,主要是用来帮我们进行请求的拦截和响应的拦截。
在请求的拦截中我们可以携带userToken,post请求头、qs对post提交数据的序列化等。
在响应的拦截中,我们可以在接收到响应后先做一层操作,如根据状态码判断登录状态、授权,根据状态码来进行错误的统一处理等等。
axios接口的统一管理,是做项目时必须的流程。这样可以方便我们管理我们的接口,在接口更新时我们不必再返回到我们的业务代码中去修改接口。
4.axios 封装过程
4.1 安装axios
npm install axios
// or
yarn add axios
4.2 引入axios,进行二次封装
-
在项目的 src 文件夹下的 utils 文件夹下新建两个文件,分别为 request.js 文件和 manage.js 文件。其中 request.js 文件用来封装 axios,manage.js 文件用来封装 axios 常用的请求方式。
-
在 request.js 文件中引入 axios,对 axios 进行二次封装,代码如下。
在 request.js 文件中主要对 axios 实例进行配置,包括请求地址、请求头、超时时间等,接着创建请求拦截器和响应拦截器。为保障服务器接口的安全性,会要求在进行 axios 封装时携带 token 调用每个接口,所以在请求拦截器中进行 token 配置,最后暴露 axios 实例。
// request.js // 引入axios import axios from 'axios' import router from '../router' // ant design的全局提示插件 import { message } from "ant-design-vue"; /** * 【指定 axios的 baseURL】 * 如果手工指定 baseURL: '/api' * 则映射后端域名,通过 vue.config.js * @type {*|string} */ let apiBaseUrl = window._CONFIG['domianURL'] || "/api"; // 根据不同环境设置不同的请求地址 if (process.env.NODE_ENV === 'production') { // 生产模式 apiBaseUrl = '' } else if (process.env.NODE_ENV === 'development') { // 开发模式 apiBaseUrl = '' } console.log("apiBaseUrl= ", apiBaseUrl) // 创建 axios 实例 const service = axios.create({ // 请求地址 baseURL: apiBaseUrl, // 设置默认get和post的请求头 headers: { get: { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8' }, post: { 'Content-Type': 'application/json;charset=utf-8' } }, // 是否跨站点访问控制请求 withCredentials: false, timeout: 600000, // 请求超时时间 // 请求数据转换 transformRequest: [(data) => { return JSON.stringify(data) }], // 响应数据转换 transformResponse: [(data) => { if (typeof data === 'string' && data.startsWith('{')) { data = JSON.parse(data) } return data }] }) // 添加请求拦截器 service.interceptors.request.use(function (config) { // 发请求之前做些什么 根据业务场景 有的需要添加请求头 // console.log(config); //获取到了所有的请求配置项 const token = localStorage.getItem("token") // 判断是否存在token,如果存在的话,则每个http header都加上token if (token) { // 将token设置到请求头中 config.headers.Authorization = 'Bearer ' + token } return config; }, function (error) { // 作用: 如果请求失败, 统一放在响应拦截器这里处理 error.data = {} error.data.msg = '服务器异常,请联系管理员!' return Promise.reject(error); }) // 添加响应拦截器 service.interceptors.response.use((response) => { return response.data }, error => { if (error.response) { console.log("------异常响应------", error.response) // let msg = showStatus(error.response.status) // alert(msg) switch (error.response.status) { case 400: alert('请求错误(400)') break case 401: // alert('登录过期请重新登录!') alert(error.response.data) // 退出登录 localStorage.removeItem('token') // 跳转到登录页面 router.push('/login') break default: alert('连接出错,请检查网络或联系管理员!') } } return Promise.reject(error); }) const showStatus = (status) => { let message = '' switch (status) { case 400: message = '请求错误(400)' break case 401: message = '未授权,请重新登录(401)' // 退出登录 localStorage.removeItem('token') // 跳转到登录页面 router.push('/login') break case 403: message = '拒绝访问(403)' break case 404: message = '请求出错(404)' break case 408: message = '请求超时(408)' break case 500: message = '服务器错误(500)' break case 501: message = '服务未实现(501)' break case 502: message = '网络错误(502)' break case 503: message = '服务不可用(503)' break case 504: message = '网络超时(504)' break case 505: message = 'HTTP版本不受支持(505)' break default: message = `连接出错(${status})!` } return `${message},请检查网络或联系管理员!` } // 对外暴露 export { service as axios }
4.3 封装 axios 请求方式
-
在项目的 src 文件夹下的 utils 文件夹下新建manage.js 文件,manage.js 文件用来封装 axios 常用的请求方式。
-
在 manage.js 文件中引入 request.js 文件,对 axios 常用的请求方式进行封装。代码如下:
// manage.js axios请求封装
import { axios } from '@/utils/request'
/**
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
* */
// get
export function getAction(url, parameter) {
return axios({
url: url,
method: 'get',
params: parameter
})
}
// post
export function postAction(url, parameter) {
return axios({
url: url,
method: 'post',
data: parameter
})
}
// post
export function postFormAction(url, parameter) {
return axios({
url: url,
method: 'post',
params: parameter
})
}
// post method= {post | put}
export function httpAction(url, parameter, method) {
return axios({
url: url,
method: method,
data: parameter
})
}
// put
export function putAction(url, parameter) {
return axios({
url: url,
method: 'put',
data: parameter
})
}
// deleteAction
export function deleteAction(url, parameter) {
return axios({
url: url,
method: 'delete',
params: parameter
})
}
/**
* 下载文件 用于excel导出
* @param url
* @param parameter
* @returns {*}
*/
export function downFile(url, parameter, token) {
return axios({
url: url,
params: parameter,
method: 'get',
responseType: 'blob',
headers: {
['X-Access-Token']: token,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Headers': '*',
}
})
}
4.4 api 接口统一管理
-
在项目的 src 文件夹下新建一个 api 文件夹,里面有多个根据模块划分的接口 js 文件, 每个 js 文件都用来管理不同模块的接口。
-
例如,在 api 文件夹下新建一个有关登录功能的 login.js 文件,用来管理所有有关登录的接口。
/** * 登录接口统一管理 */ // 导入 request 中创建的 axios 实例 // import { axios } from '@/utils/request' // 导入 manage 中封装的请求方法 import { postAction } from "@/utils/manage"; const userApi = { login: '/api/login', logout: '/api/logout' } // 第一种方式 export const login = data => postAction(userApi.login, data) // 登录 export const logout = () => postAction(userApi.logout, {}) // 退出登录 // 第二种方式 // export function login(parameter) { // return axios({ // url: userApi.login, // method: 'post', // data: parameter // }) // } // export function logout(parameter) { // return axios({ // url: userApi.logout, // method: 'post', // data: parameter // }) // }
4.5 在页面中的使用
-
可以按需导入使用
// 在页面中的使用 import { login, logout } from '@/api/login' // 登录 login({ name: '小米', password: '123456' }).then(res => { console.log(res); }) // 退出登录 logout().then(res => { console.log(res); })
-
也可以挂载到 Vue.prototype 上,全局使用
import Vue from 'vue' import App from './App.vue' // 导入路由文件 import router from './router' // 导入 vuex 文件 import store from './store/index' import { axios } from '@/utils/request' // 导入 manage 中封装的请求方法 import { postAction } from "@/utils/manage" import { login, logout } from '@/api/login' // 将 axios 挂载到 Vue 的原型上 Vue.prototype.$axios = axios Vue.prototype.$postAction = postAction Vue.prototype.$login = login
-
在组件中使用
this.$postAction('/api/login', { name: '小米', password: '123456'}).then(res => { console.log(res) }) this.$login({ name: '小米', password: '123456'}).then(res => { console.log(res) })
4.6 设置代理
什么是跨域:协议、域名、端口号不同请求,称之为跨域
http://localhost:8080/#/home ---- 前端项目本地服务器
http://39.98.123.211 ----后台服务器
解决可以使用:JSONP、CROS、代理。
proxy设置中间代理,可以帮我们向服务器发起请求。跨域问题是由浏览器的原因引起。
在 vue.config.js 文件里面进行设置
// vue.config.js
module.exports = {
// 关闭eslint
lintOnSave: false,
// 代理跨域
devServer: {
//proxy:{'/api':{}},代理器中设置/api,项目中请求路径为/api的替换为target
proxy: {
'/api': {
// http://192.168.0.19:8082/api
target: 'http://192.168.0.19:8082/api',//代理地址,这里设置的地址会代替axios中设置的baseURL
changeOrigin: true,// 如果接口跨域,需要进行这个参数配置
//ws: true, // proxy websockets
//pathRewrite方法重写url
pathRewrite: {
'^/api': '/'
//pathRewrite: {'^/api': '/'} 重写之后url为 http://192.168.1.16:8085/xxxx
//pathRewrite: {'^/api': '/api'} 重写之后url为 http://192.168.1.16:8085/api/xxxx
}
}
}
}
}