前言
在web开发的各种业务场景中,http请求无疑是家常便饭,任何前后端交互数据都需要通过http请求进行传输。同时我们知道,HTTP响应有很多状态码,对应着很多种不同的服务处理结果。在这种情况下,如果项目中每一处http请求中我们都要对各种响应结果做以判断的话,那么整个工程中就会出现大量重复代码,这样做会带来不少问题,比如:重复劳动,代码结构不清晰、核心逻辑不明确,一旦对错误处理的需求变更就要对每一处进行修改、费时费力且容易遗漏。
针对这样的情况,在以AngularJs为前端框架的项目中,我们可以使用拦截器interceptor对全局HTTP错误进行统一处理。
相关背景知识
AngularJs的拦截器interceptor
- 功能:任何时候如果我们想要为请求添加全局功能,例如身份验证、错误处理等,在请求发送给服务器之前或者从服务器返回时可以对其进行拦截。
- 本质:$http服务的基础中间件,用来向应用的业务流程中注入新的逻辑。
- 核心:服务工厂factory。通过向$httpProvider.interceptors数组中添加服务工厂,在$httpProvider中进行注册。
服务工厂factory()
服务提供了一种能在应用的整个生命周期内保持数据的方法,它能够在控制器之间进行通
信,并且能保证数据的一致性。服务是一个单例对象,在每个应用中只会被实例化一次(被$injector实例化),并且是延迟加载的(需要时才会被创建)。服务提供了把与特定功能相关联的方法集中在一起的接口。服务的工厂函数用来生成一个单例的对象或函数,这个对象或函数就是服务,它会存在于应
用的整个生命周期内。当我们的AngularJS应用加载服务时,这个函数会被执行并返回一个单例
的服务对象。服务的工厂函数既可以是一个函数也可以是一个数组,比如:
1
2
3 // 用方括号声明工厂
angular.module('myApp.services', [])
.factory('githubService', [function($http) { }]);顺便提一下,控制器controller的声明也是这样,可以是函数或数组,比如:
1 controller: function(){};或
1 controller: [function() {}]在AngularJS应用中,factory()方法是用来注册服务的最常规方式。。factory()函数可以接受两个参数。
name
:(字符串),需要注册的服务名。getFn
:(函数),这个函数会在AngularJS创建服务实例时被调用。
- 种类
一共有四种拦截器,两种成功拦截器,两种失败拦截器。request
一个新的设置对象,它需要返回一个更新过的设置对象,或者一个可以返回新的设置对象的promise。AngularJS通过$http设置对象来对请求拦截器进行调用。它可以对设置对象进行修改,或者创建
response
一个新的响应,它需要返回一个更新过的响应,或者一个可以返回新响应的promise。AngularJS通过$http设置对象来对响应拦截器进行调用。它可以对响应进行修改,或者创建
requestError
AngularJS会在上一个请求拦截器抛出错误,或者promise被reject时调用此拦截器。
responseError
AngularJS会在上一个响应拦截器抛出错误,或者promise被reject时调用此拦截器。
AngularJs的$httpProvider
$httpProvider是用来修改AngularJs中$http服务的默认行为的。它有两个属性:defaults和interceptors。
其中,defaults是一个包含了全局$http服务请求的参数值的对象,主要有这几个属性:defaults.cache
defaults.xsrfCookieName
defaults.xsrfHeaderName
defaults.headers
defaults.paramSerializer
而interceptors则是一个数组,是一个服务工厂的数组。它包含了所有的同步/异步$http中对请求进行预处理或者对响应进行后处理的服务工厂。
AngularJs模块加载前的配置config()
在模块的加载阶段,AngularJS会在提供者注册和配置的过程中对模块进行配置。在整个AngularJS的工作流中,这个阶段是唯一能够在应用启动前进行修改的部分。
config()函数接受一个参数:configFunction(函数):AngularJS在模块加载时会执行这个函数。如:1
2
3
4
5
6
7angular.module('myApp', [])
.config(function($routeProvider) {
$routeProvider.when('/', {
controller: 'WelcomeController',
template: 'views/welcome.html'
});
})
AngularJs的$q服务
$q是AngularJs中的一个服务,可以用来异步执行函数,并在异步请求执行完成时使用返回的结果。$q的使用和ES6(ES2015)中的promise很类似。关于promise的学习,推荐阮一峰的ES6标准入门。关于$q服务的详细信息,参考这一篇AngularJs中$q服务(service)的使用,这里不过多介绍。
在拦截到HTTP后,我们需要使用$q服务通过返回一个rejection来阻止下一步的默认行为。
AngularJs的$on服务
$on用于监听事件变化,比如状态改变事件:1
2
3
4
5$scope.$on('$stateChangeStart',
function(evt, toState, roParams, fromState, fromParams) {
// 可以阻止这一状态完成
evt.preventDefault();
});
我们可以通过如上方法自定义监听事件,第一个字符串参数是事件名称,第二个是监听事件发生时的回调函数。我们使用这种监听,当拦截到http错误时,向下broadcast广播这个事件,从而触发一些其他事件(比如使用弹窗提醒错误信息)。关于$broadcast和$emit,参考这一篇AngularJs的$emit和$broadcast事件分析。
AngularJs的运行块run()方法
它是所有AngularJS应用中第一个被执行的方法。运行块是AngularJS中与平时所说“main()方法”最接近的概念。运行块通常用来注册全局的事件监听器。例如,我们会在.run()块中设置路由事件的监听器以及过滤未经授权的请求。假设我们需要在每次路由发生变化时,都执行一个函数来验证用户的权限,放置这个功能唯一合理的地方就是run方法。
在拦截HTTP请求的过程中,我们在整个应用的module上注册一个这样的运行块,通过拦截,将错误信息保存到$rootScope,并由Box组件负责展示。
HTTP错误拦截器的实现
创建错误拦截服务工厂
1 | angular.module('portalApp').factory('errorInterceptor', function($q,$rootScope) { |
向$httpProvider配置错误拦截器
1 | angular.module('portalApp').config([ '$httpProvider', function($httpProvider) { |
注册运行块run,监听错误结果进行回调处理
1 | angular.module('portalApp', [ 'ngComponentRouter', ...及其他依赖注入模块]) |
参考:《AngularJs权威教程》、AngularJs官方文档