vue--axios 拦截器的简单介绍及使用场景

1.axios 拦截器简单介绍

// 添加一个请求拦截器
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });
// 添加一个响应拦截器
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });

如果之后想移除拦截器你可以这么做

var myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

2.vue 添加 axios 拦截器

1. 安装 axios

npm install axios --save-dev

2. 新建文件 axios.js

import axios from 'axios'
// 配置默认的 host, 假如你的 API host 是: http://api.htmlx.club
axios.defaults.baseURL = 'http://api.htmlx.club'
// 添加请求拦截器
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. 在 main.js 中进行引用, 并配置一个别名 ($ajax) 来进行调用:

import axios from 'axios'
import '../axios.js'    //axios.js 的路径
Vue.prototype.$ajax = axios

4. 应用, 一个登录的 post 如:

this.$ajax({
  method: 'post',
  url: '/login',
  data: {
    'userName': 'xxx',
    'password': 'xxx'
  }
}).then(res => {
  console.log(res)
})

3. 使用场景

3.1 axios 拦截器对路由进行拦截

针对登录拦截逻辑来看一下

1. 路由拦截

首先在定义路由的时候就需要多添加一个自定义字段 requireAuth, 用于判断该路由的访问是否需要登录. 如果用户已经登录, 则顺利进入路由, 否则就进入登录页面.

const routes = [
    {
        path: '/',
        name: '/',
        component: Index
    },
    {
        path: '/repository',
        name: 'repository',
        meta: {
            requireAuth: true,  // 添加该字段, 表示进入这个路由是需要登录的
        },
        component: Repository
    },
    {
        path: '/login',
        name: 'login',
        component: Login
    }
];

定义完路由后, 我们主要是利用 vue-router 提供的钩子函数 beforeEach()对路由进行判断.

router.beforeEach((to, from, next) => {
    if (to.meta.requireAuth) {  // 判断该路由是否需要登录权限
        if (store.state.token) {  // 通过 vuex state 获取当前的 token 是否存在
            next();
        }
        else {
            next({
                path: '/login',
                query: {redirect: to.fullPath}  // 将跳转的路由 path 作为参数, 登录成功后跳转到该路由
            })
        }
    }
    else {
        next();
    }
}

其中, to.meta 中是我们自定义的数据, 其中就包括我们刚刚定义的 requireAuth 字段. 通过这个字段来判断该路由是否需要登录权限. 需要的话, 同时当前应用不存在 token, 则跳转到登录页面, 进行登录. 登录成功后跳转到目标路由.

登录拦截到这里就结束了吗? 并没有. 这种方式只是简单的前端路由控制, 并不能真正阻止用户访问需要登录权限的路由. 还有一种情况便是: 当前 token 失效了, 但是 token 依然保存在本地. 这时候你去访问需要登录权限的路由时, 实际上应该让用户重新登录.

这时候就需要结合 http 拦截器 + 后端接口返回的 http 状态码来判断.

2. 拦截器

要想统一处理所有 http 请求和响应, 就得用上 axios 的拦截器. 通过配置 http response inteceptor, 当后端接口返回 401 Unauthorized(未授权), 让用户重新登录.

// http request 拦截器
axios.interceptors.request.use(
    config => {
        if (store.state.token) {  // 判断是否存在 token, 如果存在的话, 则每个 http header 都加上 token
            config.headers.Authorization = `token ${store.state.token}`;
        }
        return config;
    },
    err => {
        return Promise.reject(err);
    });
// http response 拦截器
axios.interceptors.response.use(
    response => {
        return response;
    },
    error => {
        if (error.response) {
            switch (error.response.status) {
                case 401:
                    // 返回 401 清除 token 信息并跳转到登录页面
                    store.commit(types.LOGOUT);
                    router.replace({
                        path: 'login',
                        query: {redirect: router.currentRoute.fullPath}
                    })
            }
        }
        return Promise.reject(error.response.data)   // 返回接口返回的错误信息
    });

3.2 axios 拦截器对 http 请求的响应状态统一进行处理

首先我们要明白设置拦截器的目的是什么, 当我们需要统一处理 http 请求和响应时我们通过设置拦截器处理方便很多.

这个项目我引入了 element ui 框架, 所以我是结合 element 中 loading 和 message 组件来处理的. 我们可以单独建立一个 http 的 js 文件处理 axios, 再到 main.js 中引入.

/**
 * http 配置
 */
// 引入 axios 以及 element ui 中的 loading 和 message 组件
import axios from 'axios'
import { Loading, Message } from 'element-ui'
// 超时时间
axios.defaults.timeout = 5000
// http 请求拦截器
var loadinginstace
axios.interceptors.request.use(config => {
 // element ui Loading 方法
 loadinginstace = Loading.service({ fullscreen: true })
 return config
}, error => {
 loadinginstace.close()
 Message.error({
 message: '加载超时'
 })
 return Promise.reject(error)
})
// http 响应拦截器
axios.interceptors.response.use(data => {// 响应成功关闭 loading
 loadinginstace.close()
 return data
}, error => {
 loadinginstace.close()
 Message.error({
 message: '加载失败'
 })
 return Promise.reject(error)
})
export default axios

3.3 axios 拦截器对重复请求的处理

axios 官方文档上给了两种取消请求的方式.

轻查看: vue axios 在切换路由时如何取消所有请求 --cancelToken

根据文档上的第二种方法, 我们可以在拦截器里统一处理取消重复请求

let pending = []; // 声明一个数组用于存储每个 ajax 请求的取消函数和 ajax 标识
let cancelToken = axios.CancelToken;
let removePending = (config) => {
    for(let p in pending){
        if(pending[p].u === config.url + '&' + config.method) { // 当当前请求在数组中存在时执行函数体
            pending[p].f(); // 执行取消操作
            pending.splice(p, 1); // 把这条记录从数组中移除
        }
    }
}
// 添加请求拦截器
axios.interceptors.request.use(config=>{
     removePending(config); // 在一个 ajax 发送前执行一下取消操作
     config.cancelToken = new cancelToken((c)=>{
        // 这里的 ajax 标识我是用请求地址 & 请求方式拼接的字符串, 当然你可以选择其他的一些方式
        pending.push({ u: config.url + '&' + config.method, f: c });
    });
     return config;
   },error => {
     return Promise.reject(error);
   });
// 添加响应拦截器
axios.interceptors.response.use(response=>{
      removePending(res.config);  // 在一个 ajax 响应后再执行一下取消操作, 把已经完成的请求从 pending 中移除
      return response;
   },error =>{

return { data: { } }; 返回一个空对象, 主要是防止控制台报错

});

同一个请求, 没有完成的请求将被取消

利用这个方法, 一方面可以防止重复点击不同页码导致的表格数据闪烁, 另外可以做实时搜索, 始终获取最新结果.

 

最后取消重复请求会有些问题. 第二次请求时后台已经接收到了请求. 还是会生成 2 次相同的数据. 下面是对应的处理:

let pending = []
let CancelToken = axios.CancelToken
let removePending = (config, f) => {
    let flagUrl = config.url + '&' + config.method
        if (pending.indexOf(flagUrl) !== -1) {
            if (f) {
                f() // 执行取消操作
            } else {
                pending.splice(pending.indexOf(flagUrl), 1)// 把这条记录从数组中移除
            }
        } else {
        if (f) {
            pending.push(flagUrl)
        }
    }
}
// http request 拦截器
HTTP.interceptors.request.use(
config => {
    if (config.method === 'post') {
        console.log('我是拦截')
        config.cancelToken = new CancelToken((c) => {
            removePending(config, c)
        })
    }
    return config
},
err => {
    return Promise.reject(err)
})
// http response 拦截器
HTTP.interceptors.response.use(
response => {
    if (response.config.method === 'post') {
        removePending(response.config)
    }
    return response
},
error => {
    pending = []
    return { data: {error: error} } // 返回接口返回的错误信息
})

第一种适合 tabs, 分页等快速来回点击的情况, 取消之前的请求, 保持最近的一次请求.

第二种适合相同的接口被请求了多次, 只留第一次, 其他的都取消请求.

 

https://www.h3399.cn/201805/582452.html

1.axios 拦截器简单介绍

  1. // 添加一个请求拦截器
  2. axios.interceptors.request.use(function(config){
  3. // Do something before request is sent
  4. return config;
  5. },function(error){
  6. // Do something with request error
  7. returnPromise.reject(error);
  8. });
  9. // 添加一个响应拦截器
  10. axios.interceptors.response.use(function(response){
  11. // Do something with response data
  12. return response;
  13. },function(error){
  14. // Do something with response error
  15. returnPromise.reject(error);
  16. });

如果之后想移除拦截器你可以这么做

  1. var myInterceptor = axios.interceptors.request.use(function(){/*...*/});
  2. axios.interceptors.request.eject(myInterceptor);

2.vue 添加 axios 拦截器

1. 安装 axios

npm install axios --save-dev

2. 新建文件 axios.js

  1. import axios from'axios'
  2. // 配置默认的 host, 假如你的 API host 是: http://api.htmlx.club
  3. axios.defaults.baseURL ='http://api.htmlx.club'
  4. // 添加请求拦截器
  5. axios.interceptors.request.use(function(config){
  6. // 在发送请求之前做些什么
  7. return config
  8. },function(error){
  9. // 对请求错误做些什么
  10. returnPromise.reject(error)
  11. });
  12. // 添加响应拦截器
  13. axios.interceptors.response.use(function(response){
  14. // 对响应数据做点什么
  15. return response
  16. },function(error){
  17. // 对响应错误做点什么
  18. returnPromise.reject(error)
  19. });

3. 在 main.js 中进行引用, 并配置一个别名 ($ajax) 来进行调用:

  1. import axios from'axios'
  2. import'../axios.js'//axios.js 的路径
  3. Vue.prototype.$ajax = axios

4. 应用, 一个登录的 post 如:

  1. this.$ajax({
  2. method:'post',
  3. url:'/login',
  4. data:{
  5. 'userName':'xxx',
  6. 'password':'xxx'
  7. }
  8. }).then(res =>{
  9. console.log(res)
  10. })

3. 使用场景

3.1 axios 拦截器对路由进行拦截

针对登录拦截逻辑来看一下

1. 路由拦截

首先在定义路由的时候就需要多添加一个自定义字段 requireAuth, 用于判断该路由的访问是否需要登录. 如果用户已经登录, 则顺利进入路由, 否则就进入登录页面.

  1. const routes =[
  2. {
  3. path:'/',
  4. name:'/',
  5. component:Index
  6. },
  7. {
  8. path:'/repository',
  9. name:'repository',
  10. meta:{
  11. requireAuth:true,// 添加该字段, 表示进入这个路由是需要登录的
  12. },
  13. component:Repository
  14. },
  15. {
  16. path:'/login',
  17. name:'login',
  18. component:Login
  19. }
  20. ];

定义完路由后, 我们主要是利用 vue-router 提供的钩子函数 beforeEach()对路由进行判断.

  1. router.beforeEach((to,from,next)=>{
  2. if(to.meta.requireAuth){// 判断该路由是否需要登录权限
  3. if(store.state.token){// 通过 vuex state 获取当前的 token 是否存在
  4. next();
  5. }
  6. else{
  7. next({
  8. path:'/login',
  9. query:{redirect: to.fullPath}// 将跳转的路由 path 作为参数, 登录成功后跳转到该路由
  10. })
  11. }
  12. }
  13. else{
  14. next();
  15. }
  16. })

其中, to.meta 中是我们自定义的数据, 其中就包括我们刚刚定义的 requireAuth 字段. 通过这个字段来判断该路由是否需要登录权限. 需要的话, 同时当前应用不存在 token, 则跳转到登录页面, 进行登录. 登录成功后跳转到目标路由.

登录拦截到这里就结束了吗? 并没有. 这种方式只是简单的前端路由控制, 并不能真正阻止用户访问需要登录权限的路由. 还有一种情况便是: 当前 token 失效了, 但是 token 依然保存在本地. 这时候你去访问需要登录权限的路由时, 实际上应该让用户重新登录.

这时候就需要结合 http 拦截器 + 后端接口返回的 http 状态码来判断.

2. 拦截器

要想统一处理所有 http 请求和响应, 就得用上 axios 的拦截器. 通过配置 http response inteceptor, 当后端接口返回 401 Unauthorized(未授权), 让用户重新登录.

  1. // http request 拦截器
  2. axios.interceptors.request.use(
  3. config =>{
  4. if(store.state.token){// 判断是否存在 token, 如果存在的话, 则每个 http header 都加上 token
  5. config.headers.Authorization=`token ${store.state.token}`;
  6. }
  7. return config;
  8. },
  9. err =>{
  10. returnPromise.reject(err);
  11. });
  12. // http response 拦截器
  13. axios.interceptors.response.use(
  14. response =>{
  15. return response;
  16. },
  17. error =>{
  18. if(error.response){
  19. switch(error.response.status){
  20. case401:
  21. // 返回 401 清除 token 信息并跳转到登录页面
  22. store.commit(types.LOGOUT);
  23. router.replace({
  24. path:'login',
  25. query:{redirect: router.currentRoute.fullPath}
  26. })
  27. }
  28. }
  29. returnPromise.reject(error.response.data)// 返回接口返回的错误信息
  30. });

3.2 axios 拦截器对 http 请求的响应状态统一进行处理

首先我们要明白设置拦截器的目的是什么, 当我们需要统一处理 http 请求和响应时我们通过设置拦截器处理方便很多.

这个项目我引入了 element ui 框架, 所以我是结合 element 中 loading 和 message 组件来处理的. 我们可以单独建立一个 http 的 js 文件处理 axios, 再到 main.js 中引入.

  1. /**
  2. * http 配置
  3. */
  4. // 引入 axios 以及 element ui 中的 loading 和 message 组件
  5. import axios from'axios'
  6. import{Loading,Message}from'element-ui'
  7. // 超时时间
  8. axios.defaults.timeout =5000
  9. // http 请求拦截器
  10. var loadinginstace
  11. axios.interceptors.request.use(config =>{
  12. // element ui Loading 方法
  13. loadinginstace =Loading.service({ fullscreen:true})
  14. return config
  15. }, error =>{
  16. loadinginstace.close()
  17. Message.error({
  18. message:'加载超时'
  19. })
  20. returnPromise.reject(error)
  21. })
  22. // http 响应拦截器
  23. axios.interceptors.response.use(data =>{// 响应成功关闭 loading
  24. loadinginstace.close()
  25. return data
  26. }, error =>{
  27. loadinginstace.close()
  28. Message.error({
  29. message:'加载失败'
  30. })
  31. returnPromise.reject(error)
  32. })
  33. exportdefault axios

3.3 axios 拦截器对重复请求的处理

axios 官方文档上给了两种取消请求的方式.

轻查看: vue axios 在切换路由时如何取消所有请求 --cancelToken

根据文档上的第二种方法, 我们可以在拦截器里统一处理取消重复请求

  1. let pending =[];// 声明一个数组用于存储每个 ajax 请求的取消函数和 ajax 标识
  2. let cancelToken = axios.CancelToken;
  3. let removePending =(config)=>{
  4. for(let p in pending){
  5. if(pending[p].u === config.url +'&'+ config.method){// 当当前请求在数组中存在时执行函数体
  6. pending[p].f();// 执行取消操作
  7. pending.splice(p,1);// 把这条记录从数组中移除
  8. }
  9. }
  10. }
  11. // 添加请求拦截器
  12. axios.interceptors.request.use(config=>{
  13. removePending(config);// 在一个 ajax 发送前执行一下取消操作
  14. config.cancelToken =new cancelToken((c)=>{
  15. // 这里的 ajax 标识我是用请求地址 & 请求方式拼接的字符串, 当然你可以选择其他的一些方式
  16. pending.push({ u: config.url +'&'+ config.method, f: c });
  17. });
  18. return config;
  19. },error =>{
  20. returnPromise.reject(error);
  21. });
  22. // 添加响应拦截器
  23. axios.interceptors.response.use(response=>{
  24. removePending(res.config);// 在一个 ajax 响应后再执行一下取消操作, 把已经完成的请求从 pending 中移除
  25. return response;
  26. },error =>{

return { data: { } }; 返回一个空对象, 主要是防止控制台报错

});

同一个请求, 没有完成的请求将被取消

利用这个方法, 一方面可以防止重复点击不同页码导致的表格数据闪烁, 另外可以做实时搜索, 始终获取最新结果.

最后取消重复请求会有些问题. 第二次请求时后台已经接收到了请求. 还是会生成 2 次相同的数据. 下面是对应的处理:

  1. let pending =[]
  2. letCancelToken= axios.CancelToken
  3. let removePending =(config, f)=>{
  4. let flagUrl = config.url +'&'+ config.method
  5. if(pending.indexOf(flagUrl)!==-1){
  6. if(f){
  7. f()// 执行取消操作
  8. }else{
  9. pending.splice(pending.indexOf(flagUrl),1)// 把这条记录从数组中移除
  10. }
  11. }else{
  12. if(f){
  13. pending.push(flagUrl)
  14. }
  15. }
  16. }
  17. // http request 拦截器
  18. HTTP.interceptors.request.use(
  19. config =>{
  20. if(config.method ==='post'){
  21. console.log('我是拦截')
  22. config.cancelToken =newCancelToken((c)=>{
  23. removePending(config, c)
  24. })
  25. }
  26. return config
  27. },
  28. err =>{
  29. returnPromise.reject(err)
  30. })
  31. // http response 拦截器
  32. HTTP.interceptors.response.use(
  33. response =>{
  34. if(response.config.method ==='post'){
  35. removePending(response.config)
  36. }
  37. return response
  38. },
  39. error =>{
  40. pending =[]
  41. return{ data:{error: error}}// 返回接口返回的错误信息
  42. })

第一种适合 tabs, 分页等快速来回点击的情况, 取消之前的请求, 保持最近的一次请求.

第二种适合相同的接口被请求了多次, 只留第一次, 其他的都取消请求.

posted @ 2021-11-02 11:02  酷酷的城池  阅读(1858)  评论(0编辑  收藏  举报