使用 AngularJS HTTP 拦截器

使用 AngularJS HTTP 拦截器

Flying
2016-07-10 / 0 评论 / 131 阅读 / 正在检测是否收录...

做后端用过 Spring 拦截器的同学应该清楚为什么要用拦截器吗?其实 AngularJS 在处理与后台交互数据时也可以使用 HTTP 拦截器。如果我们想要为请求添加全局功能,例如身份认证、错误处理等,在请求发送给服务器之前或服务器返回时对其进行拦截,是比较好的实现手段。

proxy-angular.svg

定义服务

AngularJS 提供 HTTP 四种拦截器,其中两种成功拦截器(requestresponse),两种失败拦截器(requestErrorresponseError)。看下面代码:

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 ,此时可以添加固定参数(如是否实例中的 appTypeplatform)和身份验证信息,如(如是否实例中的 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 架构良好基础之上的。约定得越好,拦截器的功能越能发挥出来。所以好的团队前端和后台会把接口定义得前后端都好处理。协商!协商!再协商!重要的活说三遍。
6

评论 (0)

取消