16

私は ES6 クラスを使用して AngularJS アプリを構築しており、トレーサーは AMD 形式で ES5 にトランスパイルしています。

私のモジュールでは、インターセプター クラスをインポートしてサービスとして登録し、このサービスを module.config の $httpProvider.interceptors に登録します。

var commonModule = angular.module(moduleName, [constants.name]);

import authenticationInterceptor from './authentication/authentication.interceptor';

commonModule.service('authenticationInterceptor', authenticationInterceptor);

commonModule.config( $httpProvider =>  {
    $httpProvider.interceptors.push('authenticationInterceptor');
});

私のインターセプター クラスは$q$windowサービスの両方を注入し、後で使用するためにコンストラクターに保存します。この部分をデバッガーで追跡したところ、インジェクションは適切に行われています。

'use strict';
/*jshint esnext: true */

var authenticationInterceptor = class AuthenticationInterceptor {

    /* ngInject */
    constructor($q, $window) {
        this.$q = $q;
        this.$window = $window;
    }

    responseError(rejection) {
        var authToken = rejection.config.headers.Authorization;
        if (rejection.status === 401 && !authToken) {
            let authentication_url = rejection.data.errors[0].data.authenticationUrl;
            this.$window.location.replace(authentication_url);
            return this.$q.defer(rejection);
        }
        return this.$q.reject(rejections);
    }
}

authenticationInterceptor.$inject = ['$q', '$window'];

export default authenticationInterceptor;

401で応答する要求を行うと、インターセプターが適切にトリガーされますが、「responseError」メソッドでは、「this」変数がインターセプターではなくウィンドウ オブジェクトを指しているため、this.$qまたはthis.$window .

理由がわかりませんか?何か案は?

4

9 に答える 9

27

alexpodsが指摘したようthisに、Angular フレームワークはハンドラー関数自体への参照のみを保持し、コンテキストなしでそれらを直接呼び出すため、コンテキスト ( ) は失われます。

私は最近、TypeScript を使用したインターセプターの作成に関するブログ記事を書きました$http。これは ES6 クラスにも適用されます: AngularJS 1.x Interceptors Using TypeScript .

この投稿で説明したことを要約すると、ハンドラーで失われないようにするために、メソッドをアロー関数として定義し、コンパイルされた ES5 コードthisのクラスの関数内に直接関数を効果的に配置する必要があります。constructor

class AuthenticationInterceptor {

    /* ngInject */
    constructor($q, $window) {
        this.$q = $q;
        this.$window = $window;
    }

    responseError = (rejection) => {
        var authToken = rejection.config.headers.Authorization;
        if (rejection.status === 401 && !authToken) {
            let authentication_url = rejection.data.errors[0].data.authenticationUrl;
            this.$window.location.replace(authentication_url);
            return this.$q.defer(rejection);
        }
        return this.$q.reject(rejections);
    }
}

インターセプターを完全なプロトタイプ ベースのクラスとして作成することを強く主張する場合は、インターセプターの基本クラスを定義して拡張することができます。基本クラスは、プロトタイプ インターセプター関数をインスタンス メソッドに置き換えるため、次のようにインターセプターを記述できます。

class HttpInterceptor {
  constructor() {
    ['request', 'requestError', 'response', 'responseError']
        .forEach((method) => {
          if(this[method]) {
            this[method] = this[method].bind(this);
          }
        });
  }
}

class AuthenticationInterceptor extends HttpInterceptor {

    /* ngInject */
    constructor($q, $window) {
        super();
        this.$q = $q;
        this.$window = $window;
    }

    responseError(rejection) {
        var authToken = rejection.config.headers.Authorization;
        if (rejection.status === 401 && !authToken) {
            let authentication_url = rejection.data.errors[0].data.authenticationUrl;
            this.$window.location.replace(authentication_url);
            return this.$q.defer(rejection);
        }
        return this.$q.reject(rejections);
    }
}
于 2015-12-08T18:27:32.313 に答える
4

会話に追加するには、明示的にバインドされたクラス メソッドを含むコンストラクターからオブジェクトを返すことができます。

export default class HttpInterceptor {

   constructor($q, $injector) {
       this.$q = $q;
       this.$injector = $injector;

       return {
           request: this.request.bind(this),
           requestError: this.requestError.bind(this),
           response: this.response.bind(this),
           responseError: this.responseError.bind(this)
       }
   }

   request(req) {
       this.otherMethod();
       // ...
   }

   requestError(err) {
       // ...
   }

   response(res) {
       // ...
   }

   responseError(err) {
       // ...
   }

   otherMethod() {
       // ...
   }

}
于 2016-05-05T16:04:50.407 に答える
1

クラス プロパティでアロー関数を使用することは、ES7 の実験的な機能であることに注意してください。ただし、ほとんどのトランスパイラには問題はありません。

公式の ES6 実装に固執したい場合は、コンストラクターでメソッドを定義することにより、プロトタイプ メソッドの代わりにインスタンス メソッドを作成できます。

class AuthenticationInterceptor {
  /* ngInject */
  constructor($q, $window) {
    
    this.responseError = (rejection) => {
      const authToken = rejection.config.headers.Authorization;
      if (rejection.status === 401 && !authToken) {
        const authentication_url = rejection.data.errors[0].data.authenticationUrl;
        $window.location.replace(authentication_url);
        return $q.defer(rejection);
      }
      return $q.reject(rejection);
    };
    
  }
}

定型コードの量が減るので、私はこのソリューションが気に入っています。

  • すべての依存関係を に配置する必要はなくなりましたthis。したがって、 を使用する代わりに、 を使用this.$qできます$q
  • コンストラクターから明示的にバインドされたクラス メソッドを返す必要はありません

余分なレベルのインデントを持つことは欠点です。さらに、このメソッドは、多くのインスタンスが作成されるクラスには適していない可能性があります。その場合、より多くのメモリが消費されるためです。例えば; 1 つのページで複数回使用される可能性が高いコンポーネントのコントローラーでは、(プロトタイプ メソッドにトランスパイルされた) クラス プロパティを直接使用する方が効率的です。サービス、プロバイダ、およびファクトリについて心配する必要はありません。これらはすべてシングルトンであり、一度だけインスタンス化されるためです。

于 2016-07-06T13:08:29.010 に答える
0

アロー関数に関する他の細かい回答を補うために、Interceptor で静的ファクトリ メソッドを使用すると、少しすっきりすると思います。

export default class AuthenticationInterceptor {
 static $inject = ['$q', '$injector', '$rootRouter'];
 constructor ($q, $injector, $rootRouter) {
  this.$q = $q;
  this.$injector = $injector;
  this.$rootRouter = $rootRouter;
 }

 static create($q, $injector, $rootRouter) {
  return new AuthenticationInterceptor($q, $injector, $rootRouter);
 }

 responseError = (rejection) => {
  const HANDLE_CODES = [401, 403];

  if (HANDLE_CODES.includes(rejection.status)) {
   // lazy inject in order to avoid circular dependency for $http
   this.$injector.get('authenticationService').clearPrincipal();
   this.$rootRouter.navigate(['Login']);
  }
  return this.$q.reject(rejection);
 }
}

使用法:

.config(['$provide', '$httpProvider', function ($provide, $httpProvider) {
$provide.factory('reauthenticationInterceptor', AuthenticationInterceptor.create);
$httpProvider.interceptors.push('reauthenticationInterceptor');
}]);
于 2016-04-13T07:23:34.993 に答える
0

アロー関数を使用した作業ソリューション:

var AuthInterceptor = ($q, $injector, $log) => {
    'ngInject';

    var requestErrorCallback = request => {
        if (request.status === 500) {
          $log.debug('Something went wrong.');
        }
        return $q.reject(request);
    };

    var requestCallback = config => {
        const token = localStorage.getItem('jwt');

        if (token) {
            config.headers.Authorization = 'Bearer ' + token;
        }
        return config;
    };

    var responseErrorCallback = response => {
         // handle the case where the user is not authenticated
        if (response.status === 401 || response.status === 403) {
            // $rootScope.$broadcast('unauthenticated', response);
            $injector.get('$state').go('login');
       }
       return $q.reject(response);
    }

  return {
    'request':       requestCallback,
    'response':      config => config,
    'requestError':  requestErrorCallback,
    'responseError': responseErrorCallback,
  };
};

/***/
var config = function($httpProvider) {
    $httpProvider.interceptors.push('authInterceptor');
};

/***/    
export
default angular.module('services.auth', [])
    .service('authInterceptor', AuthInterceptor)
    .config(config)
    .name;
于 2015-09-29T23:46:14.997 に答える