关于401错误

为什么要提到401,因为响应拦截器可以处理401错误

401错误场景

1. 用户未登录,代码报401,应该回到登录页

2. 登录用户的token过期 :

怎样理解token过期?

  1. . 就是登录成功了以后,后端会返回一个token值,这个值在后续请求时带上(就像是开门钥匙),<br /> 但是,这个值一般会有**有效期**(具体是多长,是由后端决定)token过期是为了安全起见

refresh_token

说明 : 当用户登陆成功之后,返回的token中有两个值

图示说明:

image.png

作用:

image.png


响应拦截器

注意:

1. 响应拦截器可以解决401错误


2. 但是并不是所有的响应拦截器都是处理401的(只是为了提升用户体验的),像公司内部的那种

后台管理器就不需要

响应拦截器的功能

  1. 这里仅仅仅仅只列举了两条

1. 所有从后端回来的响应都会集中进入响应拦截器中,如果发生401错误就可以解决

2.解决登录失败不报错的问题

3.还可以对axios接口返回值做一个脱壳处理

什么是脱壳处理? 就是我们发现axios在处理接口返回值时,默认会自动给包裹一个data

字段,这导致我们每次在业务模块获取数据都需要写res.data.data.xxx,就特别麻烦

1.解决401错误

图示说明

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/21762447/1623774823120-22c18851-f1b2-463e-b547-04108ac75cca.png#clientId=u0721dea6-1fa3-4&from=paste&height=186&id=uc40cc699&margin=%5Bobject%20Object%5D&name=image.png&originHeight=186&originWidth=555&originalType=binary&ratio=1&size=45692&status=done&style=none&taskId=u2c24806a-2e46-46a4-817b-8c5d5320eab&width=555)

代码演示

  1. import router from '../router/auth.js'
  2. // 响应拦截器
  3. request.interceptors.response.use(function (response) {
  4. console.log('响应拦截器', response)
  5. return response
  6. }, async function (error) {
  7. // 如果发生了错误,判断是否是401
  8. console.dir(error)
  9. if (error.response.status === 401) {
  10. // 出现401就在这里面 开始处理 ---
  11. console.log('响应拦截器-错误-401')
  12. const refreshToken = store.state.tokenInfo.refresh_token
  13. // if (有refresh_token) { ---- 有refresh_token
  14. if (refreshToken) {
  15. // 1. 请求新token
  16. try {
  17. const res = await axios({
  18. url: 'http://localhost:8000/v1_0/authorizations',
  19. method: 'PUT',
  20. headers: {
  21. Authorization: `Bearer ${refreshToken}`
  22. }
  23. })
  24. console.log('请求新token', res.data.data.token)
  25. // 2. 保存到vuex
  26. store.commit('mSetToken', { // mSetToken是前面定义的mutations名字
  27. refresh_token: refreshToken,
  28. token: res.data.data.token
  29. })
  30. // 3. 重发请求
  31. // request是上面创建的axios的实例,它会自动从vuex取出token带上
  32. return request(error.config)
  33. } catch (error) {
  34. // 1. 清除token
  35. store.commit('mSetToken', {})
  36. // 2. 去到登录页(如果有token值,就不能到login)
  37. const backtoUrl = encodeURIComponent(router.currentRoute.fullPath)
  38. router.push('/login?backto=' + backtoUrl)
  39. return Promise.reject(error)
  40. }
  41. } else {
  42. // 如果没有refresh_token的时候 ----没有refresh_token
  43. // 1.去到登录页
  44. // 2.清除token
  45. store.commit('mSetToken', {})
  46. const backtoUrl = encodeURIComponent(router.currentRoute.fullPath) // 回到原来跳过来的的页面,不加?后面的一串就会到首页
  47. router.push('/login?backto=' + backtoUrl)
  48. return Promise.reject(error) // 返回错误信息
  49. }
  50. } else {
  51. return Promise.reject(error)
  52. }
  53. })

原理

image.png

它的执行顺序是: 请求接口 —> 发生401报错 —> 判断是否有refresh_token —>如果有就用fresh_token请求新的token —> 后台成功返回一个新的token给我们 —> 更新vuex —> 然后重新发送请求 —> 带上新的token请求数据

  1. 如果是异常情况: 就是没有fresh_token的情况

2.解决登录失败不报错的问题,对axios接口返回值做一个脱壳处理

注意: 这个例子中的success根据实际后端返回的数据而定,每个接口不一样,这里仅做例子

  1. // 响应拦截器中
  2. // 1. 根据后端返回的数据判断本次操作是否成功,不成功 主动报错
  3. // 2.如果成功,只返回有效数据
  4. service.interceptors.response.use(
  5. response => {
  6. if (response.data.success) {
  7. return response.data // 1. 把返回数据外面包裹的data去掉了 就不用每次写res.data.data这些这样麻烦了
  8. } else {
  9. // 如果success为false 业务出错,直接触发reject
  10. // 2. 被catch分支捕获 (是login.vue的doLogin发请求里面的try,catch)
  11. return Promise.reject(new Error(response.data.message))
  12. }
  13. },
  14. error => {
  15. console.log(error)
  16. return Promise.reject(error)
  17. }
  18. )