做后端用过 Spring 拦截器的同学应该清楚为什么要用拦截器吗?其实 AngularJS 在处理与后台交互数据时也可以使用 HTTP 拦截器。如果我们想要为请求添加全局功能,例如身份认证、错误处理等,在请求发送给服务器之前或服务器返回时对其进行拦截,是比较好的实现手段。
定义服务
AngularJS 提供 HTTP 四种拦截器,其中两种成功拦截器(request
、response
),两种失败拦截器(requestError
、responseError
)。看下面代码:
angular.module("demo")
.factory('httpInterceptor', function($q, $injector, $location) {
var msg = '';
return {
// 请求拦截
request: function(config) {
if (config.method == 'POST') {
var param = {
appType: 'mobile',
platform: 'b2c'
};
// 非登录接口
if (config.url.lastIndexOf('user/rest/login') == -1) {
config.data.token = localStorage.getItem('USER_TOKEN');
}
angular.extend(param, config.data);
}
return config;
},
// 请求错误拦截
requestError: function(err) {
msg = '请求发送失败, 请检查您的网络连接情况';
$injector.get('modal').toast(msg);
$injector.get('loader').hide();
return $q.reject(err);
},
// 响应拦截
response: function(res) {
if (res.config.method == 'POST') {
var modal = $injector.get('modal'),
code = res.data.responseCode,
needLogin = false;
msg = res.data == '' ? '' : res.data.responseMsg;
if (code != 0) {
$injector.get('loader').hide();
if (code == 2301) {
msg = '抱歉,登录超时';
needLogin = true;
} else if (code == 2302 || code == 400) {
msg = '抱歉,您未登录';
needLogin = true;
} else if (code == 401) {
msg = '抱歉,您未登录,请先登录';
modal.alert(msg, function() {
location.replace('/');
});
} else {
modal.toast(msg || '未知错误');
}
if (needLogin) {;
modal.alert(msg, function() {
sessionStorage.setItem('DIRECT_URL', location.href);
location.replace('/user/login?require=1');
});
}
}
}
return $q.resolve(res);
},
// 响应错误拦截
responseError: function(err) {
var modal = $injector.get('modal');
$injector.get('loader').hide();
switch (err.status) {
case 0:
msg = '没有连接或跨域请求,请检查网络';
break;
case -1:
msg = '远程服务器无响应';
break;
case 401:
msg = '抱歉,您暂无权限访问该服务';
break;
case 500:
msg = '服务器错误';
break;
default:
msg = '发生错误:' + err.statusText + ' ' + err.status;
break;
}
modal.toast(msg);
return $q.reject(err);
}
}
})
上面实例中,我们在服务工厂中使用了四个处理函数,return 了一个包含四个成员的对象。在实际应用中,这四个成员都不是必须的,但至少要用一个:
request
:接收一个参数,它是$http
中的标准config
对象,同时也需要返回一个标准config
,此时可以添加固定参数(如是否实例中的appType
和platform
)和身份验证信息,如(如是否实例中的 token)。requestError
:当有多个 Interceptor 的时候,requestError
会在前一个 Interceptor 抛出错误或者执行$q.reject()
时执行,接收的参数就对应的错误。在这里一般处理网络错误,如本例中提示错误,隐藏加载动画。response
:接受一个请求对象参数,可以不处理就直接返回。但如果实例后端 API 返回自定义错误,HTTP 的状态码仍然是200
,也应该在这里处理自定义错误,也可以对返回数据做一些处理。如本实例会检查后端 API 有没有登录,然后做相应处理:该模态框提示或者页面跳转。responseError
:可以处理标准的 Http 错误,如服务器没有响应时,或者 Java 经常出现的500
类错误,还可以处理 HTTP 状态码不是200
的各类自定义错误。
使用服务
拦截器的核心是服务工厂,通过向 $httpprovider.interceptors
数组中添加服务工厂在 $httpProvider
中进行注册。
如下面代码:
angular
.module("demo", [])
.config(function($httpProvider, httpInterceptor) {
$httpProvider.interceptors.push('httpInterceptor');
});
这样一来,demo 模块中所有的 http 请求都有我们封装好的功能:统一的相关请求参数,统一的登录验证及错误处理。由于我们使用了 $q
服务,所以只要关注编写 http 服务中的 success
处理函数就行了。
当然,这都是建立在后端 API 架构良好基础之上的。约定得越好,拦截器的功能越能发挥出来。所以好的团队前端和后台会把接口定义得前后端都好处理。协商!协商!再协商!重要的活说三遍。
评论 (0)