美文网首页
js Fetch返回数据res.json()报错问题

js Fetch返回数据res.json()报错问题

作者: wwmin_ | 来源:发表于2022-08-02 21:58 被阅读0次

    前言

    一直以来在简单的场景中经常使用fetch代替第三方请求库, fetch是JavaScript的原生函数, 简单、高效、快速、稳定、可定制等等诸多优点。一直也是用着很是舒服,直到有一天它竟然报错了。

    什么是Fetch?

    官方: Fetch API 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应。它还提供了一个全局 fetch() 方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。

    还原现场

    因为业务需求简单,这里只封装了get和post方法, 并且后端数据都是已默认的json格式返回

    const http = {
      apiBaseUrl: config.apiBaseUrl,
      get: function (url) {
        return new Promise((resolve, reject) => {
          fetch(this.apiBaseUrl + url, {
            method: 'GET',
            headers: {
              'Content-Type': 'application/json',
              'Accept': 'application/json',
            }
          }).then(res => res.json()).then(res => {
            resolve(res);
          }).catch(e => {
            console.error("请求失败了,详细信息:" + JSON.stringify(e));
            reject(e);
          });
        })
      },
      post: function (url, body) {
        return new Promise((resolve, reject) => {
          fetch(this.apiBaseUrl + url, {
            method: "POST",
            headers: {
              'Content-Type': 'application/json',
              'Accept': 'application/json',
            }, body: JSON.stringify(body)
          }).then(res => res.json()).then(res => {
            resolve(res);
          }).catch(e => {
            console.error("请求失败了,详细信息:" + JSON.stringify(e));
            reject(e);
          })
        })
      }
    }
    

    这样的封装貌似也没什么问题 :)。

    后端没有做统一的返回数据格式约定, 直接使用.net中的ActionResult结构返回, .net封装的应该都是很好用的 :)

    /// <summary>
    /// 模拟处理数据
    /// </summary>
    /// <returns></returns>
    [HttpPost("handle-data")]
    public ActionResult HandleData(string mockData)
    {
        // 业务逻辑
        // ......
        
        // 处理完成,直接告诉浏览器我Ok了, 状态200
        return Ok();
    }
    

    此时前端在处理完成后, 控制台面板中 Network 中也显示post成功返回200了, 但是在 Console 中却有一条红色报错信息

    请求失败了,详细信息:{}
    
    fetch error
    看到报错位置竟然是post中的catch, 可明明在Network中看到返回200了啊, 稍作镇静之后就意识到应该就是返回时数据处理报错了, 在resolve(res)上面打印也没走这个逻辑, 那就是 上一层 .then(res=> res.json()有问题。
    // 将.then(res=> res.json()) 替换成下面
    .then(res => {
       console.log(res.json());
       return res.json();
    })
    

    这样写同样报错

    可以看到时解析json报错了, 嗯, 因为我们就是没有返回任何数据, 解析自然会报错!
    既然没有值Json解析报错, 那解决办法自然就得加一层判断了(也可以让后端必须返回一个Json, 简单粗暴, 哈哈! ) , 思路是先读取值然后判断是否为空.
    但是打印res时

    // 将.then(res=> res.json()) 替换成下面
    .then(res => {
       console.log(res);
       let arrayBuffer = res.json();
       let json = res.json();
       return res.json();
    })
    
    body: ReadableStream
             locked: true
             [[Prototype]]: ReadableStream
    bodyUsed: true
    headers: Headers
    [[Prototype]]: Headers
    ok: true
    redirected: false
    status: 200
    statusText: "OK"
    type: "cors"
    url: "'http://localhost:5069/api/handle-data"
    [[Prototype]]: Response
    

    而且还有一条报错信息

    Uncaught (in promise) TypeError: Failed to execute 'json' on 'Response': body stream already read
    

    body stream already read说明流只能读取一次,
    body是一个ReadableStream数据流,必须先读取流才能看到数据, 那就看一下是否还能转换成其他格式的数据.
    查找MDN https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch#body

    Body 类定义了以下方法(这些方法都被 RequestResponse所实现)以获取 body 内容。这些方法都会返回一个被解析后的 Promise 对象和数据。

    可知有5中数据格式,因为json和text可使用js原生方法JSON.parse/JSON.stringify相互转换, 那就直接选用.text()转成字符串判断即可.

    // 将.then(res=> res.json()) 替换成下面
    .then(res => {
      let data = res.text();//转成字符串判断
      return data.then(r => {
      if (r.length === 0) return null;
      else return JSON.parse(r);
      })
    })
    

    验证结果一切又回到了正常.

    简单封装fetch 获取json或空数据

    let checkStatus = res => {
      if (res.status >= 200 && res.status < 300) return res;
      else {
        let err = new Error(res.statusText);
        err.response = res;
        throw err;
      }
    }
    let parseJson = res => {
      let data = res.text();
      return data.then(r => {
        if (r.length === 0) return null;
        else return JSON.parse(r);
      })
    }
    const http = {
      apiBaseUrl: config.apiBaseUrl,
      get: function (url) {
        return new Promise((resolve, reject) => {
          fetch(this.apiBaseUrl + url, {
            method: 'GET',
            headers: {
              'Content-Type': 'application/json',
              'Accept': 'application/json',
            }
          }).then(checkStatus).then(parseJson).then(res => {
            resolve(res);
          }).catch(e => {
            console.error("请求失败了,详细信息:" + JSON.stringify(e));
            reject(e);
          });
        })
      },
      post: function (url, body) {
        return new Promise((resolve, reject) => {
          fetch(this.apiBaseUrl + url, {
            method: "POST",
            headers: {
              'Content-Type': 'application/json',
              'Accept': 'application/json',
            }, body: JSON.stringify(body)
          }).then(checkStatus).then(parseJson).then(res => {
            resolve(res);
          }).catch(e => {
            console.error("请求失败了,详细信息:" + JSON.stringify(e));
            reject(e);
          })
        })
      }
    }
    

    嗯 , 前端能处理的问题就不麻烦后端了 :)

    总结

    fetch返回的是数据流, 最终是什么格式数据需要我们自己判断使用, arrayBuffer/blob/formData/json/text 这些格式都有其使用场景, 前端er不要将思维只限定在了json哦.

    作者:wwmin
    联系:wwei.min@163.com
    公众号: DotNet技术说

    欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。如有问题或建议,请多多赐教,非常感谢。

    相关文章

      网友评论

          本文标题:js Fetch返回数据res.json()报错问题

          本文链接:https://www.haomeiwen.com/subject/ejjjwrtx.html