Interceptores en VUEJS
23/06/2018 por alvar0hurtad0

Front.id

El patrón de diseño interceptor se utiliza cuando en un determinado sistema, se quiere que alguno de sus componentes pueda modificar su comportamiento de manera completamente transparente a los componentes que lo utilizan.

Y esto en la práctica ¿para qué sirve?

La respuesta es que para muchas cosas, pero vamos a ver un ejemplo y al final os enlazo una aplicación en la que está el código funcionando.

El ejemplo que vamos a ver es el siguiente.

  • Tenemos una aplicación desacoplada que utiliza vuejs como frontend y drupal como backend.
  • Las llamadas a la API desde el frontend tienen que ser autenticadas, de manera que queremos añadir las cabeceras de autenticación por el método que sea (oAuth, csrf-token, basicAuth...) en nuestro caso vamos a usar Basic Auth por no complicar el ejemplo, pero podríamos usar oAuth de una manera  similar.

En un mundo un poco bestia podríamos hacer todas nuestras llamadas así:

    addTodo(payload) {
        const config = {
            headers: {
                "Authorization": "Basic " + localStorage.getItem('auth_token'),
            }
        };

        return axios.post(`${BASE_API_URL}/api/v1/todos?_format=json`, {title:payload.title, status: false}, config)
            .then((response) => Promise.resolve(response.data))
            .catch((error) => Promise.reject(error));
    },

    editTodo({id, title, status}) {
        const config = {
            headers: {
                "Authorization": "Basic " + localStorage.getItem('auth_token'),
            }
        };

        return axios.patch(`${BASE_API_URL}/api/v1/todos/${id}?_format=json`, {title: title, status: status}, config)
            .then((response) => Promise.resolve(response.data))
            .catch((error) => Promise.reject(error));
    },

    removeTodo(id) {
        const config = {
            headers: {
                "Authorization": "Basic " + localStorage.getItem('auth_token'),
            }
        };

        return axios.delete(`${BASE_API_URL}/api/v1/todos/${id}?_format=json`, config)
            .then((response) => Promise.resolve(response.data))
            .catch((error) => Promise.reject(error));
    },

    getTodos() {
        const config = {
            headers: {
                "Authorization": "Basic " + localStorage.getItem('auth_token'),
            }
        };
        return axios.get(`${BASE_API_URL}/api/v1/todos?_format=json`, config)
            .then((response) => Promise.resolve(response.data))
            .catch((error) => Promise.reject(error));
    },

Estoy seguro de que nuestro sexto sentido de super-programadores nos está alertando de que tenemos muchísimo código repetido y necesitamos un mecanismo que nos permita meter esas cabeceras que son todas iguales en el mismo sitio.

Hay varias opciones (una función que nos devuelva la cabecera, un wrapper de la librería axios que meta esa cabecera, etc.) pero vamos a resolverlo con un interceptor.

Lo que haremos será dejar nuestras llamadas a la API como si no necesitaran validación:

    addTodo(payload) {
        return axios.post(`${BASE_API_URL}/api/v1/todos?_format=json`, {title:payload.title, status: false})
            .then((response) => Promise.resolve(response.data))
            .catch((error) => Promise.reject(error));
    },

    editTodo({id, title, status}) {
        return axios.patch(`${BASE_API_URL}/api/v1/todos/${id}?_format=json`, {title: title, status: status})
            .then((response) => Promise.resolve(response.data))
            .catch((error) => Promise.reject(error));
    },

    removeTodo(id) {
        return axios.delete(`${BASE_API_URL}/api/v1/todos/${id}?_format=json`)
            .then((response) => Promise.resolve(response.data))
            .catch((error) => Promise.reject(error));
    },

    getTodos() {
        return axios.get(`${BASE_API_URL}/api/v1/todos?_format=json`)
            .then((response) => Promise.resolve(response.data))
            .catch((error) => Promise.reject(error));
    },

Y después nos haremos un fichero con los interceptores en el sitio que más conveniente veamos.

import axios from 'axios';

axios.interceptors.request.use(function(config) {
    const auth_token = localStorage.getItem('auth_token');
    if(auth_token) {
      config.headers.Authorization = `Bearer ${auth_token}`;
    }
    return config;
}, function(err) {
    return Promise.reject(err);
});

Es importante no olvidarse de incluir este fichero de interceptores en algún sitio coherente de nuestro código. En mi caso lo incluyo al principio del fichero donde declaro la API.

Lo más importante de todo esto es que es absolutamente transparente tanto a los componentes que utilizan la API, como a las propias funciones de la API. De manera que si cambiásemos el mecanismo de autenticación por uno más seguro, nuestros métodos addTodo, editTodo, removeTodo y getTodos no necesitarían ningún tipo de modificación.

En este caso, axios nos permite interceptar tanto los request (lo que hemos hecho) como los response. Puede verse en la documentación de la librería https://github.com/axios/axios/blob/master/README.md#interceptors

Los response son muy útiles por ejemplo en oAuth para detectar que un token está caducado y lo tenemos que renovar de manera transparente al usuario.

Aquí tenéis un ejemplo de este código funcionando:

https://github.com/alvar0hurtad0/drupal-vuejs-todo/blob/master/frontend/src/api/index.js

Espero que os haya servido, si tenéis alguna duda no dudéis en plantearla en los comentarios o por twitter @front_id 

Comentarios

Enviado por By Ronald Portocarrero (sin verificar) el Sun, 15/09/2019 - 14:52

Hola alvaro mi consulta es sencilla tengo en el back laravel, en el front vue ahora cuando hago una peticion e incluyo en las cabeceras con interceptores los headers de autorizacion mi aplicacion primero relaiza una peticion por el metodo OPTIONS y luego envia por el verbo requerido (POST, GET,....), ya configure un middleware en laravel para que responda a las peticiones y cosas demas pero aun me lanza ese inconveniente que cuando trabajo en la nube me marca un error de no contar con autorizacion. Gracias y quedo a la espera de tu respuesta.

Hola Roland,

La verdad es que necesitaría más contexto para saber qué te pasa. Si solo tienes el problema cuando sales de tu entorno local y en el caso de que el back y el front sean dominios diferentes, puede que necesites configurar tu middleware para que admita CORS.

Si quieres dame más detalles y vemos qué puede pasar.

Un saludo.

Enviado por By Antonio (sin verificar) el Tue, 03/12/2019 - 20:24

Hola Alvaro, me está ocurriendo una cosa extraña cuando utilizo el código de interceptación de axios para enviar de forma automática un token, y es que se duplican todas las peticiones, cosa que no ocurre cuando no utilizo este código.
Si chequeo a través del inspector, veo que la primera petición envía la cabecera "Request Method: OPTIONS", y la segunda "Request Method: GET" que obviamente es la correcta.
¿Tienes alguna idea de este comportamiento anómalo?

Agregar nuevo comentario

El contenido de este campo se mantiene privado y no se mostrará públicamente.

HTML Restringido

  • Etiquetas HTML permitidas: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Las líneas y los párrafos se rompen automáticamente.
  • Las direcciones de las páginas web y las direcciones de correo electrónico se convierten en enlaces automáticamente.