AngularJS - 加载器和错误处理基础设施
演示如何编写简化的 AngularJS 加载器和错误处理基础设施。
引言
本节将演示如何编写简化的 AngularJS 基础设施来处理
- UI 上的加载指示器
- 简化数据获取器 (
reponse.data
, 为什么?) - 身份验证、错误和异常处理
开始使用 Angular
Angular 应用程序通常使用一些路由器,但由 HTML 中或通过使用路由器声明控制器隐式地控制单个基本控制器,例如,我们将使用
<body ng-controller="BaseController">
这样,我们就可以基于整个应用程序中的单一关注点进行通用和常见的处理。
因此,对于我们开始来说,我们需要一个访问服务器的单点,一个将处理所有进出通信的实体。
为此,我们将创建一个 HttpInterceptor
,angular 允许我们使用以下方式拦截和注入我们的逻辑
angular.module('App.Common').factory('httpInterceptor', function ($q, $rootScope) {
return {
'request': function (config) {
},
'response': function (response) {
},
'responseError': function (rejection) {
};
});
因此,拦截器由以下模板组成
请求
:- 所有 HTTP 请求都将在此处通过一些“配置”。
响应
:- 所有服务器 200 响应都将在此处通过,此点将向应用程序发出服务器处理已完成的信号。
ResponseError
:- 所有服务器错误都将在此处通过,在这里我们将处理所有错误处理。
让我们添加实现
angular.module('App.Common').factory('httpInterceptor', function ($q, $rootScope) {
var _isLoaderDisplayed = false;
var _timer = null;
var _requestCounter = 0;
return {
'request': function (config) {
_requestCounter++;
if (!_isLoaderDisplayed) {
if (_timer != null) {
clearTimeout(_timer);
_timer = null;
}
if (config.dataFetch === undefined || config.dataFetch) {
_timer = setTimeout(function() {
_isLoaderDisplayed = true;
$rootScope.$broadcast('onLoadStart');
}, 300);
}
}
return config;
},
'response': function (response) {
_requestCounter--;
if (_requestCounter == 0) {
if (_timer != null) {
clearTimeout(_timer);
_timer = null;
}
_isLoaderDisplayed = false;
$rootScope.$broadcast('onLoadEnd');
}
// If the response is not data requested by the app service return the response as is
if (response.config.dataFetch === undefined) {
return response;
}
return response.data;
},
'responseError': function (rejection) {
_requestCounter--;
if (_timer != null) {
clearTimeout(_timer);
_timer = null;
}
_isLoaderDisplayed = false;
$rootScope.$broadcast('onLoadEnd');
$rootScope.$broadcast('onHttpError', rejection);
// do something on error (500,401)
return $q.reject(rejection);
}
};
});
所有代码都很简单,你可以在那里阅读并看到正在发生的事情。
流程是
- 请求进入
- 激活一个计时器,因此如果请求超过 300 毫秒,超时将触发
“onLoadStart
”事件 - 在响应时,我们通过计算每个请求的开始时间来检查是否所有请求都已完成,当所有请求完成后,触发“
onLoadEnd
” - 在响应错误时,触发“
onLoadEnd
”和“onHttpError
”并拒绝该 promise。
注意:我们添加了一个特殊标志:dataFetch
。此标志“使”onLoadStart
事件“静音”,因此在需要时,服务器调用是静默的。另请注意,在响应时,我们返回“response.data
”以消除在每个响应上都这样做并反复使用的操作。
接下来,angular 实现将在“BaseController
”上,该控制器在开始时被提及,我在 $rootScope
上添加了一个标志来指示应用程序是否处于“Loading
”状态,我们稍后将使用 HTML 加载器元素绑定该标志。
function BaseController($scope, $rootScope, $timeout, PopupFactory) {
$rootScope.isLoading = false;
$scope.$on('onLoadStart', function (event, data) {
$timeout(function() {
$scope.$apply(function() {
$rootScope.isLoading = true;
}, 0, false);
});
});
$scope.$on('onLoadEnd', function (event, data) {
$timeout(function () {
$scope.$apply(function () {
$rootScope.isLoading = false;
});
}, 0, false);
});
$scope.$on('onHttpError', function (event, data) {
$timeout(function () {
$scope.$apply(function () {
$rootScope.isLoading = false;
});
}, 0, false);
switch (data.status) {
case 500:
PopupFactory.openError("Unhandled Error",
"An unhandled server error has occurred. Please contact R&D.");
break;
case 403:
PopupFactory.openError("Forbidden",
"You are trying to access forbidden path. Please contact R&D.");
break;
case 401:
location = "your'e login page url"
break;
default:
PopupFactory.openError("Unknown Error",
"An error has occurred. Please contact R&D.");
break;
}
});
}
这部分将拦截器与控制器集成,它侦听来自之前和处理每个事件的事件
onLoadStart - 设置 isLoading = true
onLoadEnd - 设置 isLoading = false
onHttpError - 设置 isLoading = false
并处理错误代码 500 - 错误,403 - 禁止访问,401 - 重定向到登录页面,以及一个默认值以涵盖所有情况(所有都显示某种错误弹出窗口)。
注意:在接下来的文章中,我将解释“业务异常”的概念,并将扩展其中的一些功能。
最后一步是 HTML
<div ng-controller="BaseController">
<div class="loading" ng-show="isLoading">
<img src="/Content/images/LOADING .gif" />
</div>
@*REST OF THE APP HTML*@
</div>
CSS
/*LOADING START*/
.loading {
position: fixed;
z-index: 999999;
height: 2em;
width: 2em;
overflow: show;
margin: auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
/* Transparent Overlay */
.loading:before {
content: '';
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.3);
}
/* :not(:required) hides these rules from IE9 and below */
.loading:not(:required) {
/* hide "loading..." text */
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
/* Animation */
@-webkit-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-moz-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-o-keyframes spinner {
0%; {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
摘要
在几行代码中,我们编写了所有 Web 应用程序都需要的、加载和错误处理的基础设施基本实现。