zoukankan      html  css  js  c++  java
  • 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, 分页等快速来回点击的情况, 取消之前的请求, 保持最近的一次请求.

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

  • 相关阅读:
    (七)android开发中两种方式监听短信的原理和实现
    (三)android中Toast的使用
    (二)、Android ListView滑动过程中图片显示重复错位闪烁问题解决
    (一)PagerAdapter、FragmentPagerAdapter、FragmentStatePagerAdapter的区别
    (六)Android中使用CountDownTimer实现倒计时功能
    (五)在android 4.4上设置手机状态栏的背景
    (四)使用PagerSlidingTabStrip和ViewPager实现可左右滑动和点击效果功能
    devexpress表格gridcontrol实现列统计,总计,平均,求和等。
    常用GDB命令行调试命令
    新浪微博SSO授权后回调客户端没有执行sinaweiboDidLogIn&无法返回应用
  • 原文地址:https://www.cnblogs.com/chengfengchi/p/15497878.html
Copyright © 2011-2022 走看看