Angular http interceptors configuration

2019-09-13 01:45发布

问题:

I am building a project in ionic and I need to send jwt tokens with each request. I am a total angular newbie and I wonder where I need to place the logic for http interceptors. Where should I do that, should I put that inside config part, make a new service or something else?

This is the interceptor logic I need to insert:

$httpProvider.interceptors.push(['$q', '$location', '$localStorage', function ($q, $location, $localStorage) {
   return {
       'request': function (config) {
           config.headers = config.headers || {};
           if ($localStorage.token) {
               config.headers.Authorization = 'Bearer ' + $localStorage.token;
           }
           return config;
       },
       'responseError': function (response) {
           if (response.status === 401 || response.status === 403) {
               $location.path('/signin');
           }
           return $q.reject(response);
       }
   };
}]);

This is the config part in my app.js:

.config(function($stateProvider, $urlRouterProvider, $authProvider, ApiEndpoint) {

  $authProvider.loginUrl = ApiEndpoint.url + '/authenticate';

  $stateProvider
  .state('main', {
    url: '/main',
    abstract: true,
    templateUrl: 'templates/main.html'
  })

  .state('main.auth', {
    url: '/auth',
    views: {
      'content': {
        templateUrl: 'templates/login.html',
        controller: 'AuthController'
      }
    }
  })

  .state('main.front', {
    url: '/front',
    views: {
      'content': {
        templateUrl: 'templates/main-front.html',
        controller: 'FrontPageController'
      }
    }
  })

  .state('main.article', {
    url: '/article/{id}',
    views: {
      'content': {
        templateUrl: 'templates/main-article.html',
        controller: 'ArticleController'
      }
    }
  });

  // if none of the above states are matched, use this as the fallback
  $urlRouterProvider.otherwise('/main/front');
});

I have added it to services.js like this, I wonder if this is the right approach?

Updated code

services.js

angular.module('coop.services', [])

.factory('ArticleService', function($http, ApiEndpoint) {

  return {
    all: function() {
       return $http.get(ApiEndpoint.url + "/articles/latest").then(function(response){
             articles = response.data;
             return articles;
          });
       },
    get: function(id) {
      return this.all().then(function(response) {
        var articles = response;
        for (var i in articles) {
          if (articles[i].id == id) {
            return articles[i];
          }
        }
        return {};
      })
    }
  };
})

.factory('AuthenticationInterceptor', function RequestInterceptor($q, $location, $localStorage, $rootScope, CoreConfig) {
    var service = this;

    service.request = function (config) {
      config.headers = config.headers || {};
         if ($localStorage.token) {
             config.headers.Authorization = 'Bearer ' + $localStorage.token;
         }
         return config;
     };

    service.responseError = function (response) {
      if (response.status === 401 || response.status === 403) {
             $location.path('/signin');
         }
         return $q.reject(response);
    };

    return service;
});

.config part in app.js:

.config(function($stateProvider, $urlRouterProvider, $authProvider, ApiEndpoint, $httpProvider) {

  $httpProvider.interceptors.push('AuthenticationInterceptor');
  $authProvider.loginUrl = ApiEndpoint.url + '/authenticate';

  $stateProvider
  .state('main', {
    url: '/main',
    abstract: true,
    templateUrl: 'templates/main.html'
  })

  .state('main.auth', {
    url: '/auth',
    views: {
      'content': {
        templateUrl: 'templates/login.html',
        controller: 'AuthController'
      }
    }
  })

  .state('main.front', {
    url: '/front',
    views: {
      'content': {
        templateUrl: 'templates/main-front.html',
        controller: 'FrontPageController'
      }
    }
  })

  .state('main.article', {
    url: '/article/{id}',
    views: {
      'content': {
        templateUrl: 'templates/main-article.html',
        controller: 'ArticleController'
      }
    }
  });

  // if none of the above states are matched, use this as the fallback
  $urlRouterProvider.otherwise('/main/front');
});

回答1:

The Interceptors are usually configured at the bootstrapping phase.
I tend to handle it under the app config:

appName.config(["$httpProvider", ($httpProvider: ng.IHttpProvider) => {
                $httpProvider.interceptors.push(() => {

               // Your interceptor's logic here

           });
       });


回答2:

"An interceptor is simply a factory() service that returns an object with 4 properties that map to functions". So write your interceptor as a normal service with which methods you need overide (request, response, requestError, responseError).

In code example below, I just care to request and respondError property so I just return a service with two propertis. You can also made many interceptors to handle each kind of these property. Many interceptors can be applied to only one property (some kind of interceptor: authentication, handle error, restore request, pre-process response/request data...).

app.factory('AuthenticationInterceptor', function RequestInterceptor($rootScope, CoreConfig) {
    var service = this;

    service.request = function (config) {
        if (angular.isDefined(CoreConfig.TokenKeyString) && angular.isDefined(CoreConfig.SessionKeyString)) {
            config.headers['Authorization-Token'] = CoreConfig.TokenKeyString;
            config.headers.SessionKeyString = CoreConfig.SessionKeyString;
        }
        return config;
    };

    service.responseError = function (response) {
        return response;
    };

    return service;
});

then push your interceptor at config phase:

appp.config(['$httpProvider', function ($httpProvider) {
     $httpProvider.interceptors.push('AuthenticationInterceptor');
}]);