解决node项目一个极度困难的捕获异常却无法读取异常信息的问题

这个项目是集成了第三方NeteaseCloudMusicApi项目的接口代码,我没有直接使用它的接口,因为需要再跑一个npm run开个端口,感觉很麻烦。

所以下定决心,使用拆分代码的方式,硬生生将这个api项目的部分api接口代码集成到了我自己的项目。当然前提是必须能看懂它的代码,然后才能做拆分,否则你根本无从下手

最后终于都搞定了,这个过程很复杂,把它的api代码拆分到了我自己的项目中。

但是有个问题,有时候请求歌曲的时候会报404错误

 但是这个404错误报的明显不正确,因为我已经在代码里打了埋点

 上面证明:这个接口请求实际已经匹配到路由了,所以绝不应该是404错误!

然后排查代码发现这个第三方api的错误处理代码居然写的是404!

这就不对了,肯定起码应该是个500,但是这还不够,起码得知道是什么错误,

所以,把它的msg的值写成捕获到的err

 本以为这样处理就应该能看到具体的异常信息了,但是实际返回结果却还是空对象:

 问题变得棘手了,异常明明捕获到了啊可是为什么打印不出来呢!

看来首先得看看这个err的原始信息是什么? 于是在catch代码块的开头先console.log一下这个err:

然后这个异常信息被打印出来了:

报错如下:

get Err==> TypeError: Cannot read property 'sort' of undefined
    at Object.module.exports [as module] (D:\VueCode\VueProject\myqqmusic\src\registerRouter\NetEaseApi\modules\song_url.js:33:10)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async D:\VueCode\VueProject\myqqmusic\src\registerRouter\NetEaseApi\routes.js:69:40
 

 但是为什么把err赋值给msg就取不到值呢,然后断点调试看了一下这个error的数据结构:

 然后结合GPT分析了一下,按理说: 通过err.message和err.stack应该可以获取到异常的具体信息:

 然后确实看到了异常的具体信息了,这回没有报空值{}:

 

 好了,小结一下:Javascript中Error对象必须通过message或者stack属性来获取到具体信息,

也就是err.message或者err.stack这种形式,不能把Error对象直接赋值给变量!!!否则变量获取的值就是空对象!

但是这种报错信息太难看了,应该根据报错指引,找到异常被触发的地方:

get Err==> TypeError: Cannot read property 'sort' of undefined
    at Object.module.exports [as module] (D:\VueCode\VueProject\myqqmusic\src\registerRouter\NetEaseApi\modules\song_url.js:33:10)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async D:\VueCode\VueProject\myqqmusic\src\registerRouter\NetEaseApi\routes.js:69:40

 根据上面报错信息,找到song_url.js第33行:

这个地方说result没有sort属性,那么result本身极有可能也是个undefined!

所以非常有必要看看它的父级res到底是个什么东西

然后通过断点调试可以看到res底下果然没有data属性,那么result就必然是个undefined了,

难怪result.sort会触发Cannot read property 'sort' of undefined异常了

而且这里可以看到引发异常的真实原因:

// 这个异常信息是放在res.body里面的

 {
  code: -460,
  message: "网络太拥挤,请稍候再试!",
}

 所以在通过res.body.data获取result之前,要先判断一下res.body是否有data这个属性

或者说判断res的返回状态是否正常,因为仅仅根据res.body是否有data属性来判断返回是否正常有点太片面了

然后,我找了一个请求成功的例子:

可以看到请求成功的时候:不仅res.body有data属性,res.body.code还等于200,

所以应该增加以下代码:在res请求失败的时候主动抛出异常,并将res.body作为异常信息:

if (res.body.code != 200 ) {    

    throw new Error(res.body)   // 因为异常的详情信息是存储在res.body里面

  }

但是在前面说的外部异常拦截的地方(catch err代码块),得到结果是:msg没能正确解构出异常的信息,显示的是: [object Object]

 

 

 关于这个问题,我查了很久也查了很多资料,尝试了很多次,都没有能够解决问题。

可以说:这是这次问题排查中难度最大的地方

然后呢,我就尝试着从chatgpt对话,希望能找到答案。

最初按照它给的方法试了好几次,也还是不行。

最后我问了一个关键的神提问:

你没懂我意思,比如原始异常的信息是res.body="{"code":-460,"message":"网络太拥挤,请稍候再试!"}" ,然后我throw new Error(JSON.stringify(res.body)) 抛出了这个异常,我问catch的时候怎么才能把这json格式的异常信息取出来

 然后chatgpt终于给出了正确的回答:

我明白你的问题了。如果你的异常信息是一个JSON字符串,你可以在catch块中使用JSON.parse来解析它,以获取原始的JSON对象。以下是一个示例:

try {
  const res = { body: '{"code":-460,"message":"网络太拥挤,请稍候再试!"}' };
  throw new Error(JSON.stringify(res.body));
} catch (error) {
  try {
    // 尝试将异常消息解析为JSON
    const errorData = JSON.parse(error.message);
    console.log("Error code:", errorData.code);
    console.log("Error message:", errorData.message);
  } catch (parseError) {
    // 处理解析JSON失败的情况
    console.error("Error parsing JSON:", parseError);
  }
}

在这个示例中,我们首先抛出一个异常,异常消息是JSON字符串。然后在catch块中,我们尝试使用JSON.parse将异常消息解析为JSON对象,并访问其中的属性。如果解析失败,我们捕获到解析错误并进行处理。

这样你就可以获取到原始的JSON对象信息了。但要小心处理JSON解析失败的情况,因为异常消息不一定总是有效的JSON字符串。

这里的关键有两点:

1.  自定义异常信息时: new Error构造函数通常接收一个字符串作为错误信息的参数,而不是一个对象

如果是接收参数是对象必须通过JSON.stringify()方法转换成json字符串

2. 当已预知捕获的error是json字符串时,那么此时必须使用JSON.parse()方法把error.message转换成object对象,否则打印出来的就是[object Object]

 所以,代码需要这样修改:

异常主动抛出的部分:

  if (res.body.code != 200 ) {

    // 注意这里new Error的参数不能是对象,而必须是字符串!!!

    // 所以res.body必须通过JSON.stringify转成字符串

    throw new Error(JSON.stringify(res.body))  

  }

 异常捕获的地方:

catch (err) {

                // 因为err.message是JSON字符串,必须通过JSON.parse转换成对象,

               // 否则msg: errData赋值时就会变成: Error: [object Object]

                // 这样msg就获取不到值,变成了空值了

                const errData = JSON.parse(err.message)  //关键点 :必须使用 JSON.parse方法

                console.log("Error message:", errData.message);       

                console.log("get Err==>", err)

                console.log('[ERR]', decode(req.originalUrl), {

                    status: err.status,

                    body: err.body,

                })

                if (!err.body) {

                    res.status(500).send({

                        code: 500,

                        data: null,

                        msg: errData,                    

                    })

                    return

                }

问题解决:前台msg信息终于正常显示出来了,不再是[object Object]

 

 总结:

1. Javascript中的Error对象必须通过message或者stack属性来获取到它的具体信息,

也就是err.message或者err.stack这种形式。

不能把整个Error对象直接赋值给变量!!!否则变量获取的值就是空对象!

2.  自定义异常信息时: new Error构造函数通常接收一个字符串作为错误信息的参数,而不是一个对象

如果是接收参数是对象必须通过JSON.stringify()方法把该对象转换成json字符串

// 主动抛出异常的部分

// 如果是接收参数是对象必须通过JSON.stringify()方法把该对象转换成json字符串

new Error(JSON.stringify(err_obj))

3. 当已预知捕获的error是json字符串时,那么此时必须使用JSON.parse()方法把error.message转换成object对象,否则打印出来的就是[object Object]

// 捕获异常的部分

catch(err) {

// 当已知err是json字符串时,则必须使用JSON.parse()方法把error.message转换成object对象,否则打印出来的就是[object Object]

   errData = JSON.parse(err.message)  // 使用JSON.parse方法是关键

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/106598.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

构造类型详解及热门题型结构体大小的计算

在编写程序时,简单的变量类型已经不能满足程序中各种复杂数据的需求,因此c语言还提供了构造类型的数据,构造数据是有基本数据按照一定的规则组成的。 目录 结构体类型的概念 结构体变量的定义 结构体变量的初始化 结构体变量的引用 结构…

软件工程——期末复习知识点汇总

本帖的资料来源于某国内顶流高校的期末考试资料,仅包含核心的简答题,大家结合个人情况,按需复习~ 总的来说,大层面重点包括如下几个方面: 软件过程需求工程 设计工程软件测试软件项目管理软件过程管理 1.掌握软件生命…

flutter 使用FlutterJsonBeanFactory工具遇到的问题

如下图,使用FlutterJsonBeanFactory工具生成的数据类 但是其中 生成的 import package:null/,导致的错误:Target of URI doesn’t exist: ‘package:null/generated/json/asd.g.dart’ 尝试过的方法: 手动添加包名,…

Map集合 遍历:lambda方式

package day01;import java.util.*;public class Mapday1 {public static void main(String[] args) {/* HashMap 无序 不重复&#xff0c;会覆盖前面 无索引*/System.out.println("--------------------");Map<String, Integer> map new HashMap<>();m…

Kafka - 消息队列的两种模式

文章目录 消息队列的两种模式点对点模式&#xff08;Point-to-Point&#xff0c;P2P&#xff09;发布/订阅模式&#xff08;Publish/Subscribe&#xff0c;Pub/Sub&#xff09; 小结 消息队列的两种模式 消息队列确实可以根据消息传递的模式分为 点对点模式发布/订阅模式 这两…

Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能

CameraX是JetPack库之一&#xff0c;通过CameraX可以向应用增加相机的功能。在下列内容中&#xff0c;将介绍一个结合CameraX实现一个简单的拍照应用。本应用必须采用Android SDK 34。并通过该简单示例&#xff0c;了解传统View层次组件的UI组件如何与Compose组件结合实现移动应…

【代码思路】2023mathorcup 大数据数学建模B题 电商零售商家需求预测及库存优化问题

各位同学们好&#xff0c;我们之前已经发布了第一问的思路视频&#xff0c;然后我们现在会详细的进行代码和结果的一个讲解&#xff0c;然后同时我们之后还会录制其他小问更详细的思路以及代码的手把手教学。 大家我们先看一下代码这一部分&#xff0c;我们采用的软件是Jupyte…

tftp服务的搭建

TFTP服务的搭建 1 先更新一下apt包 sudo apt-get update2 服务器端(虚拟机上)安装 TFTP相关软件 sudo apt-get install xinetd tftp tftpd -y3 创建TFTP共享目录 mkdir tftp_sharetftp_shaer的路径是/home/cwz/tftp_share 3.1 修改共享目录的权限 sudo chmod -R 777 tftp…

python操作MySQL、SQL注入问题、视图、触发器、事务、存储过程、函数、流程控制、索引(重点)

python操作MySQL(重要) SQL的由来&#xff1a; MySQL本身就是一款C/S架构&#xff0c;有服务端、有客户端&#xff0c;自身带了有客户端&#xff1a;mysql.exe python这门语言成为了MySQL的客户端(对于一个服务端来说&#xff0c;客户端可以有很多) 操作步骤&#xff1a; …

是谁在造谣杭州取消直播带货?

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 这个世道&#xff0c;谣言的传播成本很低&#xff1a;比如“杭州禁止直播带货”这件事。 就在今天若水跟我说&#xff1a;“杭州禁止直播是谣言了&#xff0c;辟谣了”让我也赶紧隐藏或删除内容&…

【触想智能】工控一体机与5G物联网技术结合是未来发展趋势

工控一体机也叫工业电脑一体机&#xff0c;是工业应用非常重要的一种产品。目前&#xff0c;工控一体机在工业领域的应用已经非常普及&#xff0c;在繁忙的生产车间、数字化机床、自助服务终端设备等场景中&#xff0c;我们都有看到它的身影。 工控一体机应用的普及已经潜移默化…

InstructionGPT

之前是写在[Instruction-tuning&#xff08;指令微调&#xff09;]里的&#xff0c;抽出来单独讲一下。 基本原理 在做下游的任务时&#xff0c;我们发现GPT-3有很强大的能力&#xff0c;但是只要人类说的话不属于GPT-3的范式&#xff0c;他几乎无法理解。例如&#xff0c;我们…

华为---DHCP中继代理简介及示例配置

DHCP中继代理简介 IP动态获取过程中&#xff0c;客户端&#xff08;DHCP Client&#xff09;总是以广播&#xff08;广播帧及广播IP报文&#xff09;方式来发送DHCPDISCOVER和DHCPREQUEST消息的。如果服务器&#xff08;DHCP Server&#xff09;和 客户端不在同一个二层网络(二…

通过el-tree 懒加载树,创建国家地区四级树

全国四级行政地区树数据库sql下载路径&#xff1a;【免费】全国四级地区(省市县)数据表sql资源-CSDN文库https://download.csdn.net/download/weixin_51722520/88469807?spm1001.2014.3001.5503 我在后台获取地区信息添加了限制&#xff0c;只获取parentid为当前的地…

Gloss优化

Gloss优化&#xff0c;Route – Gloss – Parameters .清除不必要的线和过孔&#xff0c;圆滑线&#xff0c;焊盘中间的线&#xff0c;把转角变成圆弧&#xff0c;自动布线总会产生一些布线效果不好、多余过孔等问题。此时可以利用allegro提供的Gloss命令对设计进行优化和调整&…

ES6新增循环对象的四种方法(通俗易懂)

在我们ES6之前&#xff0c;我们一般都是用for…in来循环对象&#xff0c;现在我们ES6为我们新增了几种方法&#xff0c;让我为大家介绍一下吧&#xff01; 1.Object.keys() 静态方法返回一个由给定对象自身的可枚举的字符串键属性名组成的数组 const obj {name:"zs&quo…

项目部署Linux步骤

1、最小化安装centos7-环境准备 安装epel-release 安装epel-release&#xff0c;因为有些rpm包在官方库中找不到。前提是保证可以联网 yum install -y epel-release 修改IP net-tools net-tool&#xff1a;工具包集合&#xff0c;包含ifconfig等命令 yum install -y net-…

Games104现代游戏引擎笔记 网络游戏进阶架构

Character Movement Replication 角色位移同步 玩家2的视角看玩家1的移动是起伏一截一截&#xff0c;并且滞后的 interpolation&#xff1a;内插值&#xff0c;在两个旧的但已知的状态计算 extrapolation&#xff1a;外插值&#xff0c;本质是预测 内插值&#xff1a;但网络随着…

零基础Linux_22(多线程)线程控制和和C++的多线程和笔试选择题

目录 1. 线程控制 1.1 线程创建(pthread_create) 1.2 线程结束(pthread_exit) 1.3 线程等待(pthread_join) 1.4 线程取消(pthread_cancel结束) 1.5 线程tid(pthread_self()) 1.6 线程局部存储(__thread) 1.7 线程分离(pthread_detach) 2. C的多线程 3. 笔试选择题 答…

双十一某宝、某东活动脚本

一、前言 双十一马上就快开始了&#xff0c;各大网购平台的优惠活动开展的如火如荼&#xff0c;羊毛党们也是摩拳擦掌&#xff0c;蠢蠢欲动。为了提高效率&#xff0c;自动化脚本应运而生&#xff0c;今天&#xff0c;小编为大家带来的就是这么三款自动化点击软件。主要是针对…