前言
最近做了一个关于富文本编辑器的模块需求,但凡涉及这个玩意儿都要脱层皮。做完这个迭代后,有了点新的想法,想在掘金上记录下一些比较实际且常用的问题处理方式。
问题描述与分析
关于这个问题的需求背景我就不具体展开了,就简单描述下针对这个问题本身的一些情况。在没有上传任何文件或者上传的文件全部删除的情况下,点击打包下载文件按钮,前端页面显示的message提示为:
查看http状态码显示的是500,后端返回的message是“未找到文件数据”。那么问题来了,为什么前端页面给的错误信息提示与后端返回的meaasge不符呢?
这时候,在前端获取返回结果失败的情况下打印信息
打印出来的结果如👇所示:
返回的是一个 HttpErrorResponse 类型,一般情况下后端返回的message会位于该类型的error中,而我们展开error获得的却是Blob对象,如果是初次遇到这种问题是不是觉得不可思议?后端接口返回的message和code等信息都不翼而飞了!
机灵的同学就会想到,既然这个下载功能是后端返回blob文件流的形式,那么前端在service层应该是做了一些处理了。没错,对于接口请求响应类型我这边设置为blob,但也仅此而已,并没有对请求失败时后端返回的message动手脚呀。
🤔再三,那么只有一种可能了,就是前端在封装好的响应拦截的文件里做了一些处理,这样也就解释了为什么页面给的错误信息是我们不想要的了。我在 auth-token.interceptor.ts 文件中找到了当http状态码为500的情况下,前端对于message的处理:
通过debug或者直接查看上述代码,我们可以发现,我们打印的error.error是Blob对象,所以肯定为object的,但是下一步给erro.error.message做序列化的时候,我们之前打印的error.error里面可没有message,只有Blob对象的大小和类型,那么代码流程就会走msg不存在的情况了,这时候我们对这段模版字符串进行解析。
在定义的statusMap里,我们发现status为500的情况下,message前端提供为“内部服务器错误“,这不正是一开始前端页面给的错误信息提示嘛!
解决方案
解决问题的突破口就在于对error.error做typeof类型判断为object的时候,再去细化一下,因为Blob是object的一种,这时候我们考虑下在error.error为Blob的情况下,msg的处理方式。
通过查找MDN,我们发现要读取Blob或者File对象的内容需要用到 FileReader 这个API。通过new这个对象创造的实例可以调用 readAsText 这个方法来读取指定的Blob中的内容,而onload事件会在读取操作完成时触发,而实例的result属性即是读取的结果。
有了解决方案,那么接下来二话不说,直接coding来实现吧!
代码实现
if (error.status === 500) {
let msg;
if (typeof error.error === 'object') {
if (error.error instanceof Blob) {
const reader = new FileReader();
reader.onload = () => {
msg = reader.result.toString();
try {
const responseErrorMsg = JSON.parse(msg);
this.msgService.error(responseErrorMsg.message);
} catch {
this.msgService.error(msg);
}
};
reader.readAsText(error.error);
return throwError(error);
}
msg = JSON.stringify(error.error.message);
} else if (typeof error.error === 'string') {
const errorTextArr = error.error.match(/[\u4e00-\u9fa5]+/gm);
if (!!errorTextArr) {
msg = errorTextArr.join(',');
}
}
if (!msg) {
msg = `${statusMap['status.' + error.status]}`;
}
this.msgService.error(msg);
}
最终,前端页面正确显示了后端返回的错误提示: