做后端用过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架构良好基础之上的。约定得越好,拦截器的功能越能发挥出来。所以好的团队前端和后台会把接口定义得前后端都好处理。协商!协商!再协商!重要的活说三遍。