Controlador de errores global Ajax con AngularJS

82

Cuando mi sitio web era 100% jQuery, solía hacer esto:

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

para configurar un controlador global para errores 401. Ahora, uso angularjs con $resourcey $httppara hacer mis solicitudes (REST) ​​al servidor. ¿Hay alguna forma de configurar de manera similar un controlador de errores global con angular?

cricardol
fuente
¿Es un posible duplicado de AngularJS Failed Resource GET ?
MilkyWayJoe
1
No, queremos hacer un controlador de error 401 global para la aplicación
cricardol
lol, ¿has considerado eso lo que quieres pero con un estado http diferente (que puedes cambiar)? De todos modos, la respuesta de pkozlowski.opensource le muestra cómo hacerlo
MilkyWayJoe
No, se parece mucho más a la respuesta de Justen ... esto no es un duplicado de la pregunta que estás hablando
cricardol

Respuestas:

97

También estoy construyendo un sitio web con angular y me encontré con el mismo obstáculo para el manejo global de 401. Terminé usando el interceptor http cuando encontré esta publicación de blog. Quizás lo encuentre tan útil como yo.

"Autenticación en una aplicación basada en AngularJS (o similar)". , software espeo

EDITAR: solución final

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
fuente
4
Necesita devolver $ q.reject (respuesta); when status == 401, para evitar un error angular ruidoso
s_t_e_v_e
1
@daniellmb. Depende. Si realmente desea ir a otra página, no solo cambiar la vista, entonces debería usar $ window. Si su página de inicio de sesión es solo otra vista y controlador con su angular, entonces puede usar $ location.path
uriDium
1
@uriDium correcto, mi punto era usar los objetos angulares proporcionados para que puedas simular y probar.
daniellmb
22
$ httpProvider.responseInterceptors ahora está obsoleto. Consulte docs.angularjs.org/api/ng.$http#description_interceptors .
quartzmo
1
Si tiene éxito, debe devolver like return response || $q.when(response);para que si la respuesta está vacía, también se devuelva un objeto de promesa.
Ashish Gaur
77

Tenga en cuenta que responseInterceptors ha quedado obsoleto con Angular 1.1.4. A continuación, puede encontrar un extracto basado en los documentos oficiales , que muestra la nueva forma de implementar interceptores.

$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');

Así es como se ve en mi proyecto usando 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
fuente
Pero no tendrá datos xhr u otra información útil en el interceptor responseError. Incluso es inutilizable decidir si es recuperable.
zw0rk
1
@ zw0rk Usted ... dentro de responseError, rejectiontiene todo lo que necesita.
Langdon
¿Esa última línea $httpProvider...queda envuelta en un config()bloque?
Delwin
de hecho, he editado mi respuesta para mostrar cómo lo hice en mi proyecto usando Coffeescript. Utilice js2coffee.org si lo prefiere en Javascript.
MikeR
¿No deberían todas las referencias responsedebajo de la responseErrorfunción en realidad ser referencias a rejection(o tal vez se debería cambiar el nombre del parámetro response?
Adam Nofsinger
16

Crea el archivo <script type="text/javascript" src="../js/config/httpInterceptor.js" ></script>con este contenido:

(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
fuente
@ThilakRaj el código anterior debe ejecutarse en cada solicitud http. Así que cree dos puntos de interrupción en Chrome, uno en la línea ´return response´ y otro en ´return $ q.reject´ para comprobar que se ejecuta como debería.
Jan-Terje Sørensen