2015-06-02 18 views
5

Zrobiłem stronę AngularJS pracującą z interfejsem API. Ten interfejs API udostępnia kilka funkcji, takich jak uwierzytelnianie (Oauth).Nieskończona pętla w przechwytywaczu

Gdy interfejs API zwraca błąd 401, oznacza to, że access_token wygasł i należy go odświeżać za pomocą refresh_token.

Stworzyłem przechwytujący w AngularJS. Jego celem jest sprawdzenie, czy wynik zwrócony przez interfejs API to błąd 401, a jeśli tak, to musi odświeżyć token, a następnie przetworzyć poprzednie odrzucone żądanie.

Problem polega na tym, że przechwytujący tworzy nieskończoną pętlę. Po drugiej nieudanej próbie początkowej powinna się zatrzymać, ale nie działa.

angular.module('myApp') 
.factory('authInterceptor', function ($rootScope, $q, $window, $injector) { 

    return { 

    // If the API returns an error 
    'responseError' : function(rejection) { 

     // If it's a 401 
     if (rejection.status == 401) { 

     var deferred = $q.defer(); 

     $injector.get('$http').post('http://my-api.local/api/oauth/token', { 
      grant_type : 'refresh_token', 
      client_id  : 'id', 
      client_secret : 'secret', 
      refresh_token : $window.sessionStorage.refresh_token 
     }, { 
      headers : { 
      'Content-Type' : 'application/x-www-form-urlencoded' 
      }, 
      transformRequest : function(obj) { 
      var str = []; 
      for(var p in obj) 
      str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); 
      return str.join("&"); 
      } 
     }) 
     // New token request successfull 
     .success(function(refreshResponse) { 

      // Processing the failed request again (should fail again because I didn't saved the new tokens) 
      $injector.get('$http')(rejection.config).success(function(data) { 

      deferred.resolve(data); 

      }) 
      .error(function(error, status) { 

      deferred.reject(); 

      }); 

      return deferred.promise(); 

     }) 
     // New token request failure 
     .error(function(error, status) { 

      deferred.reject(); 
      // $location.path('users/login'); 

      return; 

     }); 

     } 
     // If it's another errorenter code here 
     else 
     return rejection; 

    } 

    } 

}); 

Więc ten kod:

  • zaczyna się, gdy pierwszy wniosek nie
  • Odświeża tokena
  • ponawia prośbę, ale nie znowu (< - Chcę tylko, aby go zatrzymać tutaj)
  • Odświeża token
  • Ponawia żądanie, ale ponownie się nie powiedzie
  • Odświeża tokena
  • ponawia prośbę, ale nie znowu
  • etc ...
+0

To nie wygląda dobrze: 'return deferred.promise();'. Powinieneś właśnie zwrócić obiekt obietnicy 'return deferred.promise' i nie próbować go wykonywać. – user2943490

Odpowiedz

5

pracowałem na ten temat w mojej aplikacji. Twoje żądanie odświeżania musi zawierać zmienną konfiguracyjną/nagłówkową, taką jak skipIntercept: true. Następnie, gdy przechwycisz to jako nieudaną odpowiedź, możesz sprawdzić zmienną rejection.config.skipIntercept. Jeśli to prawda, przejdź bezpośrednio do $q.reject(rejection).

Gdzie masz:

if (rejection.status == 401) { 

go zmienić na:

if (rejection.status == 401 && !rejection.config.skipIntercept) { 

I wtedy nad tym:

 headers : { 
     'Content-Type' : 'application/x-www-form-urlencoded' 
    }, 

Trzeba dodać:

 skipIntercept: true, 

    headers: { 
     'Content-Type' : 'application/x-www-form-urlencoded' 
    }, 

PS. there's an existing solution można użyć.

+0

Twoje rozwiązanie działa, nie mam już nieskończonych pętli! Jeśli chodzi o "istniejące rozwiązanie", wygląda świetnie, ale nie nadaje się do odświeżania tokena, prawda? Czy istnieje sposób dostosowania go do moich potrzeb? Jestem nowy w Angular i jestem trochę zagubiony ... – Flobesst

+0

@Flobesst Spójrz na plik readme na repozytorium git, wyjaśnij, jak użyć przechwytywacza. Zasadniczo przechwytujący robi wszystko, oprócz samego żądania odświeżania. Musisz tylko skonfigurować detektor zdarzeń ('$ rootScope. $ On (...)') dla zdarzenia, które przechwytujący rzuca na 401, a następnie uruchomić żądanie odświeżenia, a następnie kontynuować od tego. – ngDeveloper