① 前端如何尽量正确地处理ajax的异常
如今前端领域是MVVM框架的天下,组件库也层出不穷,但是,并没有一个知名的组件库提供ajax异常的成熟解决方案,所以今天我们就来研讨一下,如何尽量正确地处理异常。
从业务上简单说,凡是code不是200的,都是异常。这里code可以是HTTP状态码,也可以是响应体的code,就不细究了,反正本质没差别。然后,根据code的不同,又可以细分成401 403 404 500等等。
如果你的后端伙伴以HTTP状态码表示404,以响应体code表示其他错误,而且你又无法劝说他们,那么你应在axios的拦截器里把各种情况全考虑进去,比如:
超时很简单,axios也支持,设定超时阈值即可。超时跟无响应的区别是,超时意味着HTTP三次握手成功,但是得不到响应体,浏览器知道接口是存在的,但是响应体又在规定时间内没有拿到。无响应是根本无法HTTP握手,也就无法获知接口存在。
处理超时,通常做法是在拦截器里重新请求一遍,还是超时的话就视为服务器错误。
得不到响应又分成2种,可能是网断了,也可能是服务器停机了。
苛刻地说,你应分辨这2种情况,并给出不同的提示,毕竟网断了,用户可以寻找别的联网方式,而服务器停机了就给个重连按钮,让用户有事没事的尝试重连一下。
关于解决方案,首先说,XHR对象无法区分到底断网还是服务器停机,axios对于2种情况都返回'Network Error'。在得到这个反馈之后,你接下来可以有这2种解决办法:
你可以将 https://api.map..com/images/blank.gif 改成其他服务器稳定且字节小的图片。或许你可以做一张几字节的图片,传到一个非常牛逼的CDN上。
MDN手册: https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator/connection
它不支持IE,就算你不在乎这一点,那么它是不是一定准呢?对于需要登录的VPN网络,它是否准呢?我也不知道,总体说,它真的不是最佳的方案。我推荐用方案1。
很简单,分为3种:
通常,一个接口,只需要按照其中一种去处理即可,优先级就是上面书写顺序。
容器内错误提示肯定是内容区的接口出错才会出现。
处理方式:
局部报错比较容易理解,比如一个List的接口出错,那么,上策是应当给这个容器尽量撑高到有内容时的高度,然后居中给一个错误图标和错误描述。中策是不考虑有内容时候的高度,只让错误提示和错误描述撑起一定高度即可。都不算错。如果容器很小,比如就是一个3位数值,那么用一个 - 表示错误也可以。
页面整体报错稍微复杂,比如一个左右结构的内容管理系统,前置接口有userInfo接口、全局字典接口、全局路由接口等,这些接口与众不同的地方在于它们是基础接口,它们出错的话,网站干脆就不能用,页面骨架也是错乱的,这种情况下可以有2种解决办法:一是跳转到专门的5xx报错页面,页面中央有错误图标、错误提示,以及“返回上一页”的按钮;二是用白板遮罩覆盖浏览器视口,居中放一个错误图标和错误文字以及“刷新页面”的按钮,本质是用一个fixed的遮罩压住浏览器全部面积。用哪种方案都可。所以你要做的是决定哪些接口属于全局报错,哪些接口属于局部报错,并做不同的处理。
报错内容:
根据ajax异常的分类,可能至少能分出3种:网络错误、服务器宕机、服务器错误。具体用什么图标和文字我就不多说了。
组件化:
容器内报错应尽量组件化。该放返回上一页或刷新按钮的,一定要放按钮。
排他性:
只要做了容器内报错,就不要做另外报错了。这也说明了一点,就是在axios拦截器里弹toast或者modal是愚蠢的方案,我在别的文章也提到过这种观点。不做容器内报错的情况,才应该考虑其他3种情况。
什么样的场景下使用容器隐藏?
比如页面有一个角落显示你的粉丝数、关注数、评论量……。如果有获取到数据,则让这个容器出现,没有的话,则容器就保持隐藏。这一类场景往往应用于非主要内容,比如侧边栏的小内容块。
由于这只适用于非主要内容,那么主要内容也会有它自己的报错,所以,你不必担心用户看不到“网络出错”这类错误提示。
先简单对比一下toast和modal。
很简单,toast就是轻提示,不需要手动关闭,modal就是重提示,需要手动关闭。采用哪个,只要站在用户角度思考问题就好了。比如有人说,异常应当用重提示。可以这么绝对化么?不可以。比如你在某个页面点赞,提示你 “您已经点过赞了” ,这用重提示吗?肯定toast就够了。同样的,成功提示一定用轻提示吗?比如提示 “感谢参与,工作人员将在3~5个工作日内联系你” ,这么长,能toast?能一闪而过?
什么接口适用弹出提示?简单说,只要跟UI显示不相干的,都最好是使用弹出提示。比如这几种场景:
先说上传数据断网之类的错误,通常用modal,因为modal能够拦截用户动作,避免重复上传,而且,还能给用户足够的时间让用户看清楚出错原因,避免无谓的重试。
然后说数据内容错误,无论是表单提交,还是点个赞,错误提示一般用toast,毕竟用户可能只是不小心填错的,看一眼然后赶紧改正就好了。
最后说401错误,有2种做法,一是用modal,因为一般要强制用户转到登录页,但是转之前也得让用户看明白为什么要转,所以可以先modal提示,点击确定就跳转到登录页;二是用toast,但是需要先跳转,然后在登录页上提示toast“请先登录”。
警告条
警告条是可关闭的、永久生命的、又不妨碍用户继续操作的弹出组件,一般在页面顶部,或者在用户操作区域的附近。什么场景用警告条?
比如的MD编辑器,你只要输入,就会自动给服务器发送数据,频率很快,有时候因为网络或者服务器的问题,会出现保存失败的可能性,这时候就会在页面顶部出现一个比较长时间的警告条,告诉你保存失败,但你依然可以继续写,什么时候网络正常了,什么时候toast才会自动消失,当然你也可以手动关闭它。
总之,toast、modal、警告条究竟什么场合使用,要根据产品、业务具体而定,要注意优先使用容器内报错和容器隐藏。
② java开发前台报错 500 Internal Server Error
500Internal Server Error 为内部服务器错误。
常见产生原因及解决办法:
服务器资源超载:即同一时间内处理器有太多的进程需要处理的时候,会出现500错误。
查到某个进程消耗过多资源,强制关闭这个进程。
2.文件权限设置错误:500错误还有可能是对文件设置了不正确的权限,如果在刚刚上传文件后出现500错误,应该主要检查文件权限设置。
③ 前端错误Uncaught TypeError: Cannot read property 'length' of null错误怎么处理
通过统计数据库中的1000多个项目,我们发现在 JavaScript 中最常出现的错误有10个。下面会向大家介绍这些错误发生的原因以及如何防止。
1. Uncaught TypeError: Cannot Read Property
这是 JavaScript 开发人员最常遇到的错误。当你读取一个属性或调用一个未定义对象的方法时,Chrome 中就会报出这样的错误。
导致这个错误发生的原因有很多,常见的一种情况是在渲染 UI 组件时,不正确地初始化状态。我们来看一个真实的应用程序中发生这种情况的例子。
以上代码有两个重要方面:
一是组件的状态(例如 this.state),在开始生命周期之前是 undefined 状态。
二是当通过异步的方式获取数据时,无论是在构造函数中 componentWillMount 中,还是在构造函数中提取 componentDidMount,组件在数据加载之前至少会渲染一次。当检测首次渲染时,会发现 this.state.items 是未定义的。此时就会出现一个错误 -“Uncaught TypeError: Cannot read property ‘map’ of undefined" in the consol”。
解决的方法很简单:在构造函数中使用合理的默认值进行状态初始化。
2. TypeError: ‘undefined’ Is Not an Object (evaluating...)
这是在 Safari 中读取属性或调用未定义对象上的方法时发生的错误,这与 Chrome 的上述错误基本相同,只是 Safari 使用不同的错误消息。
3. TypeError: Null Is Not an Object (evaluating...)
这是在 Safari 中读取属性或调用空对象上的方法时发生的错误。
在实际情况中,导致这种错误的原因之一是:在元素加载之前,就尝试在 JavaScript 中使用 DOM 元素。这是因为 DOM API 对于空白的对象引用返回 null。
任何执行和处理 DOM 元素的 JS 代码,都应该在创建 DOM 元素之后执行。JS 代码按照 HTML 中的规定自上而下进行解释。因此,如果在 DOM 元素之前存在标签,则脚本标签内的 JS 代码就会在浏览器分析 HTML 页面时执行。如果在加载脚本之前尚未创建 DOM 元素,就会出现这样的错误。
在这个例子中,我们可以通过添加一个事件侦听器来解决这个问题,事件侦听器会在页面准备就绪时通知我们。一旦 addEventListener 被触发,该 init( ) 方法就可以使用 DOM 元素。
4. (unknown): Script Error
当未捕获的 JavaScript 错误违背跨边界原则时,就会发生脚本错误。例如,如果将 JavaScript 代码托管在 CDN 上,则任何未被捕获的错误(通过 window.onerror 处理程序发出的错误,而不是 try-catch 中捕获到的错误)将仅报告为“脚本错误”。这是浏览器的一种安全措施,主要用于防止跨域传递数据的情况出现。
要获取真实的错误消息,需要执行以下操作:
Access-Control-Allow-Origin
将
Access-Control-Allow-Origin 设置为 *, 表示可以从任何域正确访问资源。* 如有必要,也可以用自己的域名进行替换,例如:
Access-Control-Allow-Origin: www.example.com。
以下是在各种环境中设置的一些示例:
在脚本标签上设置crossorigin =“anonymous”
在你的 HTML 源代码中,为每一个脚本设置
Access-Control-Allow-Origin,在设置 SCRIPT 标签中,设置 crossorigin="anonymous"。在将 crossorigin 属性添加到脚本标签之前,请确保正在向脚本文件发送 header。在 Firefox 中,如果 crossorigin 属性存在但 Access-Control-Allow-Origin 标题不存在,则脚本不会执行。
5. TypeError: Object Doesn’t Support Property
当调用未定义的方法时,IE 中会发生这样的错误。
这相当于 Chrome 中的 “undefined’ is not a function” 错误。对于相同的逻辑错误,不同的浏览器可能会有不同的错误消息。
这是在 IE 的 Web 应用程序中使用 JavaScript 命名空间出现的一个常见问题。出现这种情况的绝大部分原因是IE无法将当前名称空间内的方法绑定到this关键字。例如,如果你有 JS Rollbar 方法的命名空间 isAwesome。通常,如果位于 Rollbar 命名空间内,则可以使用以下语法调用该 isAwesome 方法:
6. TypeError: ‘undefined’ Is Not a Function
当调用未定义的函数时,Chrome 中就会发生这样的错误。
执行上面的代码会导致以下错误:“Uncaught TypeError: undefined is not a function。” 发生以上错误的原因是,当你调用 setTimeout( ) 时,实际上是在调用 window.setTimeout( ),传递给 setTimeout( ) 的匿名函数是在窗口对象的上下文中定义的,而该窗口对象没有 clearBoard( ) 方法。
符合旧版浏览器的解决方案是以变量的方式简单地将引用保存在 this 中,然后通过闭包继承。