source

AngularJS: HTTP 인터셉터에 서비스 주입(순환 의존성)

nicesource 2023. 3. 25. 11:29
반응형

AngularJS: HTTP 인터셉터에 서비스 주입(순환 의존성)

내 Angular를 위해 HTTP 인터셉터를 작성하려고 합니다.인증을 처리하기 위한 JS 앱입니다.

이 코드는 작동하지만 Angular가 자동으로 처리한다고 생각했기 때문에 수동으로 서비스를 주입하는 것이 걱정됩니다.

    app.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.interceptors.push(function ($location, $injector) {
        return {
            'request': function (config) {
                //injected manually to get around circular dependency problem.
                var AuthService = $injector.get('AuthService');
                console.log(AuthService);
                console.log('in request interceptor');
                if (!AuthService.isAuthenticated() && $location.path != '/login') {
                    console.log('user is not logged in.');
                    $location.path('/login');
                }
                return config;
            }
        };
    })
}]);

처음에는 일을 시작했지만 순환 의존성 문제에 부딪혔습니다.

    app.config(function ($provide, $httpProvider) {
    $provide.factory('HttpInterceptor', function ($q, $location, AuthService) {
        return {
            'request': function (config) {
                console.log('in request interceptor.');
                if (!AuthService.isAuthenticated() && $location.path != '/login') {
                    console.log('user is not logged in.');
                    $location.path('/login');
                }
                return config;
            }
        };
    });

    $httpProvider.interceptors.push('HttpInterceptor');
});

또 다른 우려되는 이유는 Angular Docs의 $http 섹션이 "일반적인 방법"을 HTTP 인터셉터에 주입하는 방법을 보여주는 것 같습니다.「인터셉터」아래의 코드 스니펫을 참조해 주세요.

// register the interceptor as a service
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
  return {
    // optional method
    'request': function(config) {
      // do something on success
      return config || $q.when(config);
    },

    // optional method
   'requestError': function(rejection) {
      // do something on error
      if (canRecover(rejection)) {
        return responseOrNewPromise
      }
      return $q.reject(rejection);
    },



    // optional method
    'response': function(response) {
      // do something on success
      return response || $q.when(response);
    },

    // optional method
   'responseError': function(rejection) {
      // do something on error
      if (canRecover(rejection)) {
        return responseOrNewPromise
      }
      return $q.reject(rejection);
    };
  }
});

$httpProvider.interceptors.push('myHttpInterceptor');

위의 코드는 어디에 배치해야 합니까?

제 질문은 이 일을 하는 올바른 방법이 무엇일까?

고마워요, 그리고 제 질문이 충분히 명확했으면 좋겠어요.

이게 내가 결국 하게 된 거야

  .config(['$httpProvider', function ($httpProvider) {
        //enable cors
        $httpProvider.defaults.useXDomain = true;

        $httpProvider.interceptors.push(['$location', '$injector', '$q', function ($location, $injector, $q) {
            return {
                'request': function (config) {

                    //injected manually to get around circular dependency problem.
                    var AuthService = $injector.get('Auth');

                    if (!AuthService.isAuthenticated()) {
                        $location.path('/login');
                    } else {
                        //add session_id as a bearer token in header of all outgoing HTTP requests.
                        var currentUser = AuthService.getCurrentUser();
                        if (currentUser !== null) {
                            var sessionId = AuthService.getCurrentUser().sessionId;
                            if (sessionId) {
                                config.headers.Authorization = 'Bearer ' + sessionId;
                            }
                        }
                    }

                    //add headers
                    return config;
                },
                'responseError': function (rejection) {
                    if (rejection.status === 401) {

                        //injected manually to get around circular dependency problem.
                        var AuthService = $injector.get('Auth');

                        //if server returns 401 despite user being authenticated on app side, it means session timed out on server
                        if (AuthService.isAuthenticated()) {
                            AuthService.appLogOut();
                        }
                        $location.path('/login');
                        return $q.reject(rejection);
                    }
                }
            };
        }]);
    }]);

주의:$injector.get콜은 대행 수신기의 메서드 내에 있어야 합니다.다른 곳에서 콜을 사용하려고 하면 JS에서 순환 의존관계 오류가 계속 발생합니다.

$http와 AuthService 사이에는 순환 의존 관계가 있습니다.

를 사용하여 수행하는 작업$injector서비스는 AuthService에 대한 $http 의존을 지연시킴으로써 치킨 앤 에그 문제를 해결하고 있습니다.

나는 네가 한 일이 사실 가장 간단한 방법이라고 믿는다.

다음 방법으로도 이 작업을 수행할 수 있습니다.

  • 나중에 인터셉터를 등록한다(에서 등록한다).run()대신 블록하다config()블록이 이미 효과가 있을 수 있습니다).하지만 $http가 아직 호출되지 않았음을 보장할 수 있습니까?
  • 호출을 통해 대행 수신기를 등록할 때 $http를 AuthService에 수동으로 "주입"합니다.AuthService.setHttp()뭐 그런 거.
  • ...

$injector를 직접 사용하는 것은 반대라고 생각합니다.

순환 의존 관계를 끊는 방법은 이벤트를 사용하는 것입니다.$state를 주입하는 대신 $rootScope를 주입합니다.직접 리다이렉트 하는 대신

this.$rootScope.$emit("unauthorized");

플러스

angular
    .module('foo')
    .run(function($rootScope, $state) {
        $rootScope.$on('unauthorized', () => {
            $state.transitionTo('login');
        });
    });

잘못된 논리가 그런 결과를 낳았다

실제로 사용자가 작성한 것인지 Http Interceptor가 아닌지에 대한 검색의 포인트는 없습니다.모든 HTTP 요청을 단일 .service(또는 .factory 또는 .provider)로 정리하여 모든 요청에 사용할 것을 권장합니다.함수를 호출할 때마다 사용자가 로그인했는지 여부를 확인할 수 있습니다.모든 것이 정상인 경우 전송 요청을 허용합니다.

당신의 경우, Angular 어플리케이션은 어떤 경우에도 요청을 보낼 것이고, 당신은 그곳에서 승인을 확인하기만 하면, 그 후에 JavaScript는 요청을 보낼 것입니다.

문제의 핵심

myHttpInterceptor.$httpProvider사례.당신의.AuthService$http , 「」$resource여기에는 의존관계 재귀 또는 순환 의존관계가 있습니다.에서 그 AuthService이 에러는 표시되지 않습니다.


또, @지적한 이 「@Pieter Herroelen」에 할 수 .module.run하지만 이것은 해결책이 아니라 해킹에 가까울 것이다.

깔끔하고 자기 설명적인 코드를 사용할 경우 몇 가지 솔리드 원칙을 따라야 합니다.

이러한 상황에서는 적어도 단일 책임 원칙이 많은 도움이 될 것입니다.

Auth 스테이트(is Authorized())를 체크하는 경우, 그 스테이트를 다른 모듈인 "Auth"로 하는 것을 추천합니다.이 모듈에서는 상태를 유지할 뿐 $http 자체는 사용하지 않습니다.

app.config(['$httpProvider', function ($httpProvider) {
  $httpProvider.interceptors.push(function ($location, Auth) {
    return {
      'request': function (config) {
        if (!Auth.isAuthenticated() && $location.path != '/login') {
          console.log('user is not logged in.');
          $location.path('/login');
        }
        return config;
      }
    }
  })
}])

인증 모듈:

angular
  .module('app')
  .factory('Auth', Auth)

function Auth() {
  var $scope = {}
  $scope.sessionId = localStorage.getItem('sessionId')
  $scope.authorized = $scope.sessionId !== null
  //... other auth relevant data

  $scope.isAuthorized = function() {
    return $scope.authorized
  }

  return $scope
}

(localStorage를 사용하여 클라이언트 측에 sessionId를 저장했습니다만, 예를 들어 $http 호출 후에 AuthService 내부에서 설정할 수도 있습니다.)

언급URL : https://stackoverflow.com/questions/20647483/angularjs-injecting-service-into-a-http-interceptor-circular-dependency

반응형