zoukankan      html  css  js  c++  java
  • 使用whistle模拟cgi接口异常:错误码、502、慢网速、超时

    绝大多数程序只考虑了接口正常工作的场景,而用户在使用我们的产品时遇到的各类异常,全都丢在看似 ok 的 try catch 中。如果没有做好异常的兼容和兜底处理,会极大的影响用户体验,严重的还会带来安全和资损风险。

    接口异常,通常可以分为以下三类:

    • CGI 逻辑出错。如调用方入参缺失类业务逻辑报错;

    • 服务不稳定。如服务器不稳定导致 nginx 各类 500、502,cgi 路径调整导致的 404

    • 用户网络环境差。如,网络不稳定、网速慢、运营商劫持等

    那么,我们在写代码时,如何快速的模拟这些接口异常,做好程序的兼容处理呢?

    今天向大家介绍网络调试神器 whistle 的网络异常调试方法,如果你还没用过 whistle,请参考《8102 年的程序员不需要 Hosts 和 Fiddler》。

    假设我们有以下前端页面 index.html,放置在自己的本地路径:

    <p id="success" style="color:green;"></p>
    <p id="fail" style="color:red;"></p>
    <script>
      fetch(`/mock?r=${Math.random()}`)
        .then(response => {
          return response.json()
        })
        .then(v => {
          document.getElementById('success').innerHTML = v.data;
        }).catch(err => {
          document.getElementById('fail').innerHTML = err.message;
        })
    </script>

    接下来,打开 whistle Rules 配置面板 http://127.0.0.1:8899/#rules ,配置模拟的 demo page 和 mock CGI:

    */mock file://({"code":0,"data":"success"}) # 配置 mock cgi 为模拟的 json 数据
    example.com file:///Users/kaiye/Projects/Markdown/20181213/ # 配置任意域名到本地 demo 目录,这里注意替换成自己的路径

    打开 http://example.com ,正常逻辑下页面展示出了绿色的 success ,现在我们开始加入一些网络异常。

     

    1、业务逻辑异常处理

    例如 CGI 没有返回 data 字段,而是返回了一个错误码 code 和对应的 message,针对这种业务逻辑异常我们只需在第二个 then 中做好 code 值的判断即可(注意,这里的 code、message、data 只是示例,实际业务 CGI 中的 JSON 结构体的字段名很可能不同):

    fetch(`/mock?r=${Math.random()}`)
      .then(response => response.json())
      .then((v) => {
        // 业务逻辑异常处理
        if (v.code !== 0) {
          return Promise.reject(new Error(`ERROR_LOGIC_CODE:${v.code}`));
        }
        document.getElementById('success').innerHTML = v.data;
      })
      .catch((err) => {
        document.getElementById('fail').innerHTML = err.message;
      });

    相应的 whistle 配置如下:

    */mock file://({"code":12345,"message":"some_logic_error"}) # 模拟业务逻辑异常

    2、服务器异常处理

    如果服务器直接抛出了 502 错误码,我们希望代码能给用户提示的同时,再做一个异常上报。

    fetch(`/mock?r=${Math.random()}`)
      .then((response) => {
        // 服务器异常处理
        if (response.ok) {
          return response.json();
        }
        return Promise.reject(new Error(`ERROR_STATUS_CODE:${response.status}`));
      })
      .then((v) => {
        // 业务逻辑异常处理
        if (v.code !== 0) {
          return Promise.reject(new Error(`ERROR_LOGIC_CODE:${v.code}`));
        }
        document.getElementById('success').innerHTML = v.data;
      })
      .catch((err) => {
        const [type, value] = err.message.split(':');
        // 异常类型上报
        console.log(type, value);
        document.getElementById('fail').innerHTML = err.message;
      });
     

    通过 whistle 的模拟配置如下:

    */mock statusCode://502 # 模拟 HTTP 状态码异常

    3、接口被劫持注入

    如果 CGI 被运营商劫持注入,可能导致接口返回一个不合法的 JSON 结构,最前面的 response.json() 会抛异常,我们可以提前 catch 住:

    fetch(`/mock?r=${Math.random()}`).then((response) => {
      // 服务器异常处理
      if (response.ok) {
        return (
          response
            .json()
            // 接口数据解码异常处理
            .catch(err => Promise.reject(new Error('ERROR_DECODE_JSON')))
        );
      }
      return Promise.reject(new Error(`ERROR_STATUS_CODE:${response.status}`));
    });
    ​

    whistle 模拟配置如下:

    */mock file://(<div>hijacking</div>{"code":0,"data":"success"}) # 模拟接口被劫持注入 1

    借助 htmlAppendvalues 配置,可以模拟更复杂的注入示例:

    */mock file://({"code":0,"data":"success"}) htmlAppend://{hijacking.html} # 模拟接口被劫持注入 2
    ```hijacking.html
    <script>
    alert('hijacking')
    </script>
    ```

     

    4、用户网络不稳定

    如果我们要模拟请求发出 10 秒后断网或网络不通的情况,可以通过 whistle 这样配置:

    */mock reqDelay://10000 enable://abort # 模拟 10 秒超时后网络不通

    让用户苦苦等待 10 秒,再报错的体验太糟糕。我们可以封装一个能配置超时时间的请求发送函数,同时把上面提到的错误异常都一起配置进来。

    <p id="success" style="color:green;"></p>
    <p id="fail" style="color:red;"></p>
    <script>
      function myFetch(url, configOptions) {
        const options = Object.assign(
          {
            timeout: 3000
          },
          configOptions
        )
        const { timeout } = options
        return new Promise((resolve, reject) => {
          // 超时异常处理
          const timer = setTimeout(() => {
            reject(new Error(`ERROR_TIMEOUT:${timeout}`))
          }, timeout)
          fetch(url, options)
            .then(data => {
              clearTimeout(timer)
              resolve(data)
            })
            .catch(err => {
              clearTimeout(timer)
              reject(err)
            })
        })
          .then(response => {
            // 服务器异常处理
            if (response.ok) {
              return (
                response
                  .json()
                  // 接口数据解码异常处理
                  .catch(err => Promise.reject(new Error('ERROR_DECODE_JSON')))
              )
            } else {
              return Promise.reject(
                new Error(`ERROR_STATUS_CODE:${response.status}`)
              )
            }
          })
          .then(v => {
            // 业务逻辑异常处理
            if (v.code !== 0) {
              return Promise.reject(new Error(`ERROR_LOGIC_CODE:${v.code}`))
            } else {
              return v.data
            }
          })
          .catch(err => {
            const [type, value] = err.message.split(':')
            // 异常类型上报
            console.log(type, value)
            return Promise.reject(err)
          })
      }
      myFetch(`/mock?r=${Math.random()}`)
        .then(data => {
          document.getElementById('success').innerHTML = data
        })
        .catch(err => {
          document.getElementById('fail').innerHTML = err.message
        })
    </script>

     

    这样,自定义的 myFetch 只需关注业务具体逻辑,针对不同的 catch error 做对应的处理。

    除以上提到的协议命令字外,whistle 还支持 resSpeed 用于模拟低网速传输(单位:kb/s),tpl 协议则可以根据请求传入参数来动态模拟不同的数据在 Frames 面板,还可以对 WebSocket/Socket 请求进行暂停、延迟等网络异常的模拟。

     

    小程序 fetch API 实现

    最后,留一道思考题。

    近来微信小程序开发非常火,小程序原生提供的 wx.request API 能用于发送 HTTPS 请求,请在它的基础之上进行封装,支持 promise 调用和 timeout 超时时间定义(小程序默认的请求超时定义在 app.json 中,不够灵活),并针对以上提到的 HTTP 状态码异常、接口劫持注入、慢网络、无网络状态等各种网络异常进行兼容处理。

    欢迎留言分享你的代码实现,在公众号「猫哥学前班」中回复关键词 request ,可以参考我的实现和 whistle rules 配置。

     

     

     

     

     

  • 相关阅读:
    【Android进阶】判断网络连接状态并自动界面跳转
    【Android进阶】快捷图标的创建与移除
    【Android基础】单元测试的配置
    【Android基础】短信的发送
    【Android基础】Activity之间进行参数传递的三种方式
    【Android基础】AndroidManifest常用权限permission整理
    【Android进阶】ZXing android 错误(Could not find class 'com.google.zxing.ResultPoint)
    【Android基础】点击Back键退出应用程序
    【Android进阶】Application对象的详解
    js案例_下滑列表
  • 原文地址:https://www.cnblogs.com/kaiye/p/10137592.html
Copyright © 2011-2022 走看看