详解Axios统一错误处理与后置


Posted in Javascript onSeptember 26, 2018

问题

在进行业务开发的时候,前后端会对接口的数据结构进行约定,若接口有异常,需要将异常信息展示给用户知晓。这个流程里,数据结构是确定的(事先约定),数据的处理逻辑是相同的(展示给用户),如果在业务代码代码中重复的catch(e) { 展示给用户 },就非常的不优雅。本着Don't repeat myself(懒)的原则,需要对接口错误进行统一处理。

接下来,我会结合具体的业务场景,讲一讲我的解决方案。

业务场景

  1. 后端通过http状态标识接口状态,错误信息在response的data里
  2. 前端的处理逻辑是使用element-ui的Message展示错误信息
  3. 使用axios

axios可以通过拦截器,在业务代码处理响应之前对响应进行处理,类似于下面的流程

someAPI()
  .then(interceptorsFn)
  .then(业务逻辑)

所以,我们可以在interceptors对响应进行统一处理:

request.interceptors.response.use(
  (response) => response.data,
  (error) => {
    // 针对特定的http状态码进行处理
    if (error.response && error.response.status === 401) {
      router.push({ name: 'ssoLogin' })
      return new Promise(() => {}) // pending的promise,中止promise链
    }

    .....

    const msg = error.response.data
    Message.error(msg)

    return Promise.reject(error.response)
  }
)

如何进行特定的错误处理

不难看出,上面的方案有一个问题,如果有某个接口需要有业务代码来展示定制的错误信息(这个情况十分常见),如何处理?

naive方案1:业务代码使用其它的方式展示信息:例如Notify。
这个方案被我司产品痛骂,因为破坏了统一的错误信息展示,并且此时统一的错误信息是一个垃圾信息,没必要展示。

naive方案2:业务代码直接使用Message,顶掉统一的错误信息。
这个方案还是被产品大哥(dog)怼了,因为明显的用户体验不好,错误信息出现了闪烁。

帅气的解决方案3:业务代码决定是否隐藏统一错误提示
那么问题来了,由于是先走拦截器,再走业务代码,如何由业务代码决定是否隐藏统一错误提示呢?

我的办法是,将统一的错误提示使用setTimeout放到下一个loop执行,并通过一个变量标识是否要执行统一错误提示。

request.interceptors.response.use(
  (response) => response.data,
  (error) => {
    ...
    setTimeout(() => {
      if (tag) {
        Message.error(msg)
      }
    })
  }
)

接下来,需要考虑的是,如何在业务代码里改变标识变量

naive方案1:一个全局的变量或者方法

这个方案非常的不靠谱,若在其它代码里改变了这个全局变量,就嗝屁,并且N个接口公用一个标识变量,只能是同一个状态。

帅气方案2:

request.interceptors.response.use(
  (response) => response.data,
  (error) => {
    ...
    let isShowNormalError = true
    const hideNormalError = () => isShowNormalError = false

    setTimeout(() => {
      if (isShowNormalError) {
        Message.error(msg)
      }
    })

    return Promise.reject({ ...error.response, hideNormalMessage }) // 在error.response上添加方法
  }
)

业务代码:

someAPIFN()
  .then()
  .catch({ data, hideNormalMessage }) {
    // 业务代码
    hideNormalMessage()
  }

兼容旧代码

目前的方案需要对现存代码做修改,对进行特殊处理的接口添加hideNormalMessage()。如果不想全局搜索添加代码(懒),可以根据业务来进行兼容。下面讲一下我结合业务代码进行的兼容处理(非常不推荐)。

request.interceptors.response.use(
  (response) => response.data,
  (error) => {
    // warning,和业务代码深度耦合,不推荐
    const hasMessageBeforeCatch = !!document.querySelector('.el-message')
    
    ...

    let isShowNormalError = true
    const hideNormalError = () => isShowNormalError = false

    setTimeout(() => {
      const hasMessageAfterCatch = document.querySelector('.el-message')

      // 调用catch前没有message,调用catch后有message,表示message是在catch过程中产生
      const madeMessageWhenCatch = !hasMessageBeforeCatch && hasMessageAfterCatch

      if (isShowNormalError && !madeMessageWhenCatch) {
        Message.error(msg)
      }
    })

    return Promise.reject({ ...error.response, hideNormalMessage }) // 在error.response上添加方法
  }
)

逻辑:如果在catch中使用了Message,就不展示统一错误处理

总结

这个解决方案的关键在于使用setTimeout使得统一错误处理“落后”于业务代码,并在Promise.reject的参数中添加控制函数使得业务代码可以决定是否展示统一错误处理。稍作抽象与封装就可以形成一个业务无关、框架无关的统一错误处理方案。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
客户端脚本中常常出现的一些问题和调试技巧
Jan 09 Javascript
jQuery循环滚动展示代码 可应用到文字和图片上
May 11 Javascript
js实现广告漂浮效果的小例子
Jul 02 Javascript
javascript字符串替换函数如何一次性全部替换掉
Oct 30 Javascript
原生JS实现旋转木马式图片轮播插件
Apr 25 Javascript
使用Bootstrap框架制作查询页面的界面实例代码
May 27 Javascript
javascript日期比较方法实例分析
Jun 17 Javascript
详解React Native开源时间日期选择器组件(react-native-datetime)
Sep 13 Javascript
原生JS实现列表子元素顺序反转的方法分析
Jul 02 Javascript
详解JavaScript 浮点数运算的精度问题
Jul 23 Javascript
OpenLayers3实现测量功能
Sep 25 Javascript
Javascript中async与await的捕捉错误详解
Mar 03 Javascript
Vue监听一个数组id是否与另一个数组id相同的方法
Sep 26 #Javascript
vue 循环加载数据并获取第一条记录的方法
Sep 26 #Javascript
基于vue v-for 多层循环嵌套获取行数的方法
Sep 26 #Javascript
VUE v-for循环中每个item节点动态绑定不同函数的实例
Sep 26 #Javascript
原生JS实现简单的无缝自动轮播效果
Sep 26 #Javascript
Vue中控制v-for循环次数的实现方法
Sep 26 #Javascript
web页面和微信小程序页面实现瀑布流效果
Sep 26 #Javascript
You might like
array_multisort实现PHP多维数组排序示例讲解
2011/01/04 PHP
PHP中使用Imagick实现各种图片效果实例
2015/01/21 PHP
PHP 5.3和PHP 5.4出现FastCGI Error解决方法
2015/02/12 PHP
php获取网站百度快照日期的方法
2015/07/29 PHP
DOM2非标准但却支持很好的几个属性小结
2012/01/21 Javascript
JS中自定义定时器让它在某一时刻执行
2014/09/02 Javascript
JavaScript实现基于Cookie的存储类实例
2015/04/10 Javascript
Javascript技术栈中的四种依赖注入小结
2016/02/27 Javascript
js实现的万能flv网页播放器代码
2016/04/30 Javascript
Bootstrap按钮下拉菜单组件详解
2016/05/10 Javascript
jQuery实现鼠标经过购物车出现下拉框代码(推荐)
2016/07/21 Javascript
BootStrap中Datepicker控件带中文的js文件
2016/08/10 Javascript
jQuery实现表格奇偶行显示不同背景色 就这么简单
2017/03/13 Javascript
解决浏览器会自动填充密码的问题
2017/04/28 Javascript
JavaScript实现无刷新上传预览图片功能
2017/08/02 Javascript
使用vue-aplayer插件时出现的问题的解决
2018/03/02 Javascript
使用pm2自动化部署node项目的方法步骤
2019/01/28 Javascript
Vue发布项目实例讲解
2019/07/17 Javascript
html-webpack-plugin修改页面的title的方法
2020/06/18 Javascript
详解javascript void(0)
2020/07/13 Javascript
vue 扩展现有组件的操作
2020/08/14 Javascript
[56:41]2018DOTA2亚洲邀请赛 3.31 小组赛 A组 Newbee vs OG
2018/04/01 DOTA
[56:00]2018DOTA2亚洲邀请赛 4.6 淘汰赛 VP vs TNC 第二场
2018/04/10 DOTA
解决Python的str强转int时遇到的问题
2018/04/09 Python
Selenium(Python web测试工具)基本用法详解
2018/08/10 Python
Python实现FTP弱口令扫描器的方法示例
2019/01/31 Python
pycharm实现猜数游戏
2020/12/07 Python
使用CSS Grid布局实现网格的流动
2014/12/30 HTML / CSS
金宝贝童装官网:Gymboree
2016/08/31 全球购物
Java如何调用外部Exe程序
2015/07/04 面试题
家具公司总经理岗位职责
2014/07/08 职场文书
2014年党的群众路线活动个人整改措施
2014/10/28 职场文书
个人党性锻炼总结
2015/03/05 职场文书
2019年最新版见习人员管理制度!
2019/07/08 职场文书
Nginx进程管理和重载原理详解
2021/04/22 Servers
Elasticsearch6.2服务器升配后的bug(避坑指南)
2022/09/23 Servers