Globalna obsługa błędów Ajax z AngularJS

82

Kiedy moja witryna była w 100% jQuery, robiłem to:

$.ajaxSetup({
    global: true,
    error: function(xhr, status, err) {
        if (xhr.status == 401) {
           window.location = "./index.html";
        }
    }
});

ustawić globalną procedurę obsługi błędów 401. Teraz używam angularjs z $resourcei $httprobić swoje (rest) żądania do serwera. Czy istnieje sposób, aby podobnie ustawić globalną obsługę błędów z angular?

krikardol
źródło
Czy jest to możliwy duplikat pliku GET z błędem AngularJS ?
MilkyWayJoe
1
Nie, chcemy wykonać globalną procedurę obsługi błędu 401 dla aplikacji
cricardol
lol, czy zastanawiałeś się, co chcesz, ale z innym stanem http (który możesz zmienić)? W każdym razie odpowiedź pkozlowski.opensource pokazuje, jak to zrobić
MilkyWayJoe
Nie, to bardziej przypomina odpowiedź Justena ... to nie jest duplikat pytania, o którym mówisz
cricardol

Odpowiedzi:

97

Tworzę też witrynę internetową z angularem i natknąłem się na tę samą przeszkodę w obsłudze globalnego 401. Skończyło się na używaniu przechwytywacza http, kiedy natknąłem się na ten post na blogu. Może uznasz to za pomocne, tak jak ja.

„Uwierzytelnianie w aplikacji opartej na AngularJS (lub podobnej)”. , oprogramowanie espeo

EDYCJA: ostateczne rozwiązanie

angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives'], function ($routeProvider, $locationProvider, $httpProvider) {

    var interceptor = ['$rootScope', '$q', function (scope, $q) {

        function success(response) {
            return response;
        }

        function error(response) {
            var status = response.status;

            if (status == 401) {
                window.location = "./index.html";
                return;
            }
            // otherwise
            return $q.reject(response);

        }

        return function (promise) {
            return promise.then(success, error);
        }

    }];
    $httpProvider.responseInterceptors.push(interceptor);
Justen
źródło
4
Trzeba zwrócić $ q.reject (odpowiedź); kiedy status == 401, aby uniknąć
głośnego
1
@daniellmb. To zależy. Jeśli rzeczywiście chcesz przejść do innej strony, a nie tylko zmienić widok, powinieneś użyć $ window. Jeśli twoja strona logowania to tylko kolejny widok i kontroler z twoim
angularem
1
@uriDium słusznie, moim celem było użycie dostarczonych kątowo obiektów, abyś mógł kpić i testować.
daniellmb
22
$ httpProvider.responseInterceptors jest teraz przestarzałe. Zobacz docs.angularjs.org/api/ng.$http#description_interceptors .
quartzmo
1
W przypadku powodzenia musisz zwrócić w ten return response || $q.when(response);sposób, że jeśli odpowiedź jest pusta, zwracany jest również obiekt obietnicy.
Ashish Gaur,
77

Zwróć uwagę, że responseInterceptors są przestarzałe w Angular 1.1.4. Poniżej znajduje się fragment oparty na oficjalnej dokumentacji , przedstawiający nowy sposób implementacji przechwytywaczy.

$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
  return {
    'response': function(response) {
      // do something on success
      return response || $q.when(response);
    },

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

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

Tak to wygląda w moim projekcie przy użyciu Coffeescript:

angular.module("globalErrors", ['appStateModule']).factory "myHttpInterceptor", ($q, $log, growl) ->
  response: (response) ->
    $log.debug "success with status #{response.status}"
    response || $q.when response

  responseError: (rejection) ->
    $log.debug "error with status #{rejection.status} and data: #{rejection.data['message']}"
    switch rejection.status
      when 403
        growl.addErrorMessage "You don't have the right to do this"
      when 0
        growl.addErrorMessage "No connection, internet is down?"
      else
        growl.addErrorMessage "#{rejection.data['message']}"

    # do something on error
    $q.reject rejection

.config ($provide, $httpProvider) ->
  $httpProvider.interceptors.push('myHttpInterceptor')
MikeR
źródło
Ale w odpowiedzi nie będzie żadnych danych xhr ani innych przydatnych informacji. Nie można nawet zdecydować, czy można go odzyskać.
zw0rk
1
@ zw0rk Będziesz ... wewnątrz responseError, rejectionma wszystko, czego potrzeba.
Langdon
Czy ta ostatnia linia jest $httpProvider...opakowana w config()blok?
delwin
rzeczywiście, zredagowałem odpowiedź, aby pokazać, jak zrobiłem to w moim projekcie przy użyciu Coffeescript. Jeśli wolisz, użyj js2coffee.org w Javascript.
MikeR
Gdyby nie wszystkie odniesienia do responsepod responseErrorfunkcja faktycznie odniesień do rejection(albo parametr powinien zmieniono jej nazwę response?
Adam Nofsinger
16

Utwórz plik <script type="text/javascript" src="../js/config/httpInterceptor.js" ></script>z następującą zawartością:

(function(){
  var httpInterceptor = function ($provide, $httpProvider) {
    $provide.factory('httpInterceptor', function ($q) {
      return {
        response: function (response) {
          return response || $q.when(response);
        },
        responseError: function (rejection) {
          if(rejection.status === 401) {
            // you are not autorized
          }
          return $q.reject(rejection);
        }
      };
    });
    $httpProvider.interceptors.push('httpInterceptor');
  };
  angular.module("myModule").config(httpInterceptor);
}());
Jan-Terje Sørensen
źródło
@ThilakRaj powyższy kod powinien działać przy każdym żądaniu http. Stwórz więc dwa punkty przerwania w Chrome, jeden w wierszu „zwrotna odpowiedź”, a drugi w „zwrotnym $ q.reject”, aby sprawdzić, czy działa tak, jak powinien.
Jan-Terje Sørensen