Tras tanto tiempo de inactividad vuelvo con nuevo contenido, esta vez acerca del framework javascript AngularJS. En concreto voy a describir como funciona su servicio http para comunicarnos con un backend en una webapp js y como se implementan inteceptores para poder introducirnos en la comunicación con el servidor pudiendo modificar tanto peticiones como respuestas.
AngularJS expone un servicio $http en su propio core el cual facilita la comunicación HTTP con servidores remoto a través de de peticiones ajax o via JSONP. Este servicio nos permite comunicarnos con el servidor http con los verbos tradicionales de una manera bastante sencilla. Llamando al método correspondiente podremos realizar la petición correspondiente la cual nos devolverá un objeto “promise” con dos métodos específicos; el callback de éxito y el callback de error:
1 2 3 4 5 6 7 8 |
// Petición GET $http.get('/url'). success(function(data, status, headers, config) { // callback de éxito }). error(function(data, status, headers, config) { // callback de error }); |
Interceptores HTTP
Los interceptores son manejadores que se interponen entre las request y las response que hace nuestra web app contra un servidor remoto. Un ejemplo claro de empleo de estos interceptores es con el propósito de manejar peticiones con autenticación, ya que cada petición debe de incluir la cabecera de autenticación para que el servidor sirva el contenido asociado a este usuario.
En un proyecto en el que estoy tabajando, tras loguearse la webapp contra el server se almacena en el cliente un token que se debe enviar en cada petición. No es nada recomendable incorporar en cada llamada al servicio http las cabeceras necesarias repitiendo el mismo código una vez tras otra. Recuerda “Dont Repeat Yourself”. El empleo de este manejador nos aporta la unificación de la funcionalidad en un único punto incorporando las cabeceras en cada llamada.
Si no, de otra manera deberíamos de incluir las cabeceras en cada petición como en el siguiente fragmento:
1 2 3 4 5 6 7 8 |
var config = {headers: { 'Authorization': 'Basic d2VudHsvcnRobgFuOkNoYW5nZV9tZQ==', 'Accept': 'application/json;odata=verbose', "X-Testing" : "testing" } }; $http.get("/test", config); |
También los interceptores pueden tener más propósitos aparte del de autenticación, como el del manejo global de errores http, manejo de loaders o por ejemplo el preprocesado y postprocesado de las respuestas a los controladores. A veces es deseable ser capaz de interceptar estas peticiones antes de que sean manejadas por el servidor adaptándolas a tus necesidades.
Para implementar un interceptor http en angularjs se necesita registrar una factoría la cual se inyectará en la configuración del servicio $httpProvider. Este interceptor nos permite suscribirnos a los siguientes eventos:
request:
este evento nos devuelve la configuración del objeto http permitiéndonos modificarla o crear una nuevo.requestError:
este evento es llamado justo antes de lanzar un error en la peticiónresponse:
este evento nos permite modificar el objeto respuesta recibido en una petición.responseError:
este evento es llamado justo antes de lanzar un error de respuesta
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
// Definición de la factoría $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { return { // optional method 'request': function(config) { // do something on success return 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; }, // optional method 'responseError': function(rejection) { // do something on error if (canRecover(rejection)) { return responseOrNewPromise } return $q.reject(rejection); } }; }); // configuracion del interceptor $httpProvider.interceptors.push('myHttpInterceptor'); |
A continuación muestro una implementación real la cual intercepta y añade una cabecera a todas las peticiones que vayan hacia las rutas que contengan /api/. También se implementa una lógica para cuando se reciben códigos de respuesta 401 que implican un error de autenticación, forzando a la aplicación llevarte al login:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
angular.module('starter.services', []) .factory('myHttpInterceptor', function($q,$rootScope,$location,$localStorage) { return { request: function (config) { if(config.url.indexOf("/api/") !== -1)){ isLogged=$localStorage.get('logged'); if(isLogged){ user=$localStorage.getObject('user'); config.headers['Authorization'] = 'Basic ' + user.encode; }else{ $location.path('/login'); } } return config || $q.when(config); }, responseError: function(response) { // Unauthorized if(response.status==401){ $location.path('/login'); } return $q.reject(response); } }; }) |