使用AngularJS HTTP拦截器

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

发表评论

电子邮件地址不会被公开。 必填项已用*标注