02-对于axios请求的处理(添加请求拦截器、响应拦截器以及token处理还有请求头设置)
axios请求处理
我们在配置完基本环境之后通常需要去配置好axios请求,包含配置请求的基地址、请求拦截器、响应拦截器、处理token权限以及过期问题、配置对应的请求头等等,其他操作可以去参考axios官网: www.axios-js.com/zh-cn/docs/
在src目录下创建utils文件夹,里面用于存放通用的一些工具类函数,比如说axios请求的工具配置、校验函数(日期格式化),localStorage本地存储等相关操作(存取删)
一、导入axios并设置接口的基地址
import store from '@/store' // 导入仓库,我们会对仓库里的数据进行操作
import axios from 'axios' // 导入axios对象,用于创建一个axios实例
import router from '@/router' // 导入全局路由对象,用于后面导航到具体页面
import { Message } from 'element-ui' // 导入element-ui里的message方法,用于提示
// 创建一个axios实例
const service = axios.create({
// 设置请求接口的基地址
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
上面的 process.env.VUE_APP_BASE_API
是环境变量文件里设置好的地址,三个环境变量文件在启动或者运行项目的时候只会执行一个,三个文件是项目的不同阶段启动项目的时候用:dev是开发阶段,production是产品上线阶段,stage是测试阶段?忘了
- yarn dev 会执行.env.development这个文件
- yarn build:prod 会执行.env.production这个文件
- yarn build:stage 会执行 .env.staging这个文件
这里的
VUE_APP_BASE_API
是设置代理的,设置代理之后,找不到地址的话就默认找自己的本地地址,找到本地地址访问之后,再通过proxy设置的代理地址,去访问真正的接口地址,解决跨域问题。 下面的两种书写方式是等价的,因为没写http://localhost:9090
只写api
的话,会自动补全,也就跟http://localhost:9090/api
性质一样。port是设置端口号。
在vue.config.js 文件中配置proxy代理服务器的节点
举例:封装单独的登录接口
- 基本封装
/api/user.js
中封装请求
import request from '@/utils/request'
export function reqLogin(data) {
return request({
method: 'post',
url: '/sys/login', // 这里的 /api 由于开发环境所有接口都要加, 通过baseUrl加
data
})
}
- 基于环境变量配置基地址
utils/request.js
配置基础地址
import axios from 'axios'
// 创建了axios实例, 使用的是自己的配置项
const instance = axios.create({
// 开发环境, 找 env.development, 找 VUE_APP_BASE_API 变量
// 生产环境, 找 env.production, 找 VUE_APP_BASE_API 变量
baseURL: process.env.VUE_APP_BASE_API, // 环境变量
timeout: 5000 // request timeout
})
// 请求拦截器
// 响应拦截器
export default instance
.env.development
yarn dev 加载
# base api
# 最终请求地址 /api/sys/login => http://ihrm-java.itheima.net/api/sys/login
VUE_APP_BASE_API = '/api'
.env.production
yarn build:prod 加载
# base api
# 最终请求地址: http://ihrm-java.itheima.net/api/sys/login
VUE_APP_BASE_API = 'http://ihrm-java.itheima.net/api/'
- 页面测试:在页面中导入测试
import { reqLogin } from '@/api/user'
handleLogin() {
this.$refs.loginForm.validate(async valid => {
if (valid) {
// 2.发送登录请求
const res = await reqLogin(this.loginForm)
console.log(res)
}
})
}
二、设置请求拦截器
因为我们在登录成功之后,服务器会返回一个叫token的身份令牌,我们需要用这个token令牌去访问对应的需要权限的网页,比如个人中心,或者其他私人展示的页面,或者其他需要令牌的地方。 进入这些网页通常是要发送请求的,然后就需要在每次发送请求之前都要在请求头中加入这个token字段,才可以被服务器识别并返回正确的数据。所以我们设置请求拦截器,在每次发送请求之前都携带这个令牌。
- 下面的拦截器中,是从vuex的store里拿到预先存储好的token,并按照要求添加在了请求头中,这里是在token前面还拼接了一个Bearer字段。store里面的token是从本地localStorage里面取出来的,本地存储的token参数是在登录成功的时候后台返回给我们,我们在那个时候存进去。存进去的是一个对象tokenInfo,里面有一个token,一个retoken。我们拿这个token进行操作,token的时效一般是2小时,2小时之后token会过期,访问会退出,当我们检测到是因为token过期的原因导致请求失败的话,那么我们就要使用retoken重新向后台发送数据,拿到一个拥有新时效的token,继续拥有2小时时效,再继续发送请求并返回到刚才的页面。这个retoken也有时效,但是持续时间长一点,大概能有一周左右。
// 添加请求拦截器
service.interceptors.request.use(function(config) {
// 在发送请求之前做些什么
// 拿到token,添加到请求头中
const token = store.getters.token
if (token) config.headers.Authorization = `Bearer ${token}`
return config
}, function(error) {
// 对请求错误做些什么
return Promise.reject(error)
})
三、设置响应拦截器
下面的响应拦截器,首先是帮助初步判断是否请求成功,并且进行相关提示。请求成功之后根据返回值进行相关的操作,并且可以对响应数据进行初步结构,比如从response里结构出data,剥离一层,或者两层,return出去,那我们拿到的数据就简化了很多,省的后面反复结构取值。 响应错误的处理:响应错误会有很多种,我们这里是做了一个对token是否过期的判断,如果返回的状态码status===401并且data.code===10002,那就是说token过期了,这个规则是后端接口规定的。token过期了的一种处理就是拿着retoken向后台请求拿到新的token并存进本地,再重新请求返回刚才的页面。这里的处理是发现token过期了,手动清除掉本地存储的token值,并通过路由跳转到login登录页面进行重新登录,重新拿token值。
// 添加响应拦截器
service.interceptors.response.use(function(response) {
// 对响应数据做点什么
const { message, success } = response.data
if (success) {
Message.success(message)
} else {
Message.error(message)
return Promise.reject(new Error(message))
}
return response
}, function(error) {
console.dir(error)
// 对响应错误做点什么
// 如果响应的状态码是401,并且response.data.code是10002 说明token过期了, 让用户重新登录
if (error.response.status === 401 && error.response.data.code === 10002) {
// 1. 清除个人信息和token 触发logout的action
store.dispatch('user/logout')
// 2. 跳转到首页
router.push('/login')
// 3. 提示用户
Message.error('登录超时正在退出...')
} else {
Message.error('服务器异常')
}
return Promise.reject(error)
})
四、导出处理过的service方法,其他地方要用就直接导入,导入的名字自己定义就行,本质上还是这个处理过的service
export default service
转载自:https://juejin.cn/post/7070847898832863269