Axios: Interceptores: cómo evitar que los mensajes interceptados se resuelvan como error

Creado en 14 mar. 2016  ·  33Comentarios  ·  Fuente: axios/axios

Estoy tratando de hacer un interceptor para las respuestas 401 que resultan del token caducado. Tras la intercepción, quiero iniciar sesión y volver a intentar las solicitudes con el nuevo token. Mi problema es que el inicio de sesión también se realiza de forma asincrónica, por lo que cuando ocurre el reintento, las promesas originales se rechazan. ¿Hay alguna manera de evitar eso? Aquí está mi código:

axios.interceptors.response.use(undefined, err => {
  if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
    refreshLogin(getRefreshToken(),
      success => {
        setTokens(success.access_token, success.refresh_token)
        err.config.__isRetryRequest = true
        err.config.headers.Authorization = 'Bearer ' + getAccessToken()
        axios(err.config)
      },
      error => { console.log('Refresh login error: ', error) }
    )
  }
})

Comentario más útil

Podrías hacer algo como esto:

axios.interceptors.response.use(function (response) {
  return response;
}, function (error) {

  const originalRequest = error.config;

  if (error.response.status === 401 && !originalRequest._retry) {

    originalRequest._retry = true;

    const refreshToken = window.localStorage.getItem('refreshToken');
    return axios.post('http://localhost:8000/auth/refresh', { refreshToken })
      .then(({data}) => {
        window.localStorage.setItem('token', data.token);
        window.localStorage.setItem('refreshToken', data.refreshToken);
        axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.token;
        originalRequest.headers['Authorization'] = 'Bearer ' + data.token;
        return axios(originalRequest);
      });
  }

  return Promise.reject(error);
});

Todos 33 comentarios

Perdón por la demora, acabo de ver este problema.

Debe hacer una declaración de retorno de su interceptor que mantendrá viva la Promesa. Más o menos cambie su refreshLogin para devolver una Promesa y luego devuélvala de su interceptor.

O si no puede refactorizar refreshLogin , puede envolverlo en una Promesa.

axios.interceptors.response.use(undefined, err => {
  let res = err.response;
  if (res.status === 401 && res.config && !res.config.__isRetryRequest) {
    return new Promise((resolve, reject) => {
      refreshLogin(getRefreshToken(),
        success => {
          setTokens(success.access_token, success.refresh_token)
          err.config.__isRetryRequest = true
          err.config.headers.Authorization = 'Bearer ' + getAccessToken()
          resolve(axios(err.config))
        },
        error => {
          console.log('Refresh login error: ', error)
          reject(error)
        }
      )
    });
  }
})

No, no lo hace.

La línea "let res = err.response" no funciona - no hay respuesta en err.

Si lo dejo fuera, entonces el éxito se activa, entonces el inicio de sesión ocurre, pero la solicitud original no se vuelve a intentar.

¿Qué versión de axios estás usando?

Eso fue con 0.12.0.
Ahora que actualicé a 0.13.1, err ha convertido en una cadena con pila de llamadas.

Error: la solicitud falló con el código de estado 401
en createError (eval en...

@ dmt0 ¿Conseguiste que esto funcionara?

@heeton No,

Podrías hacer algo como esto:

axios.interceptors.response.use(function (response) {
  return response;
}, function (error) {

  const originalRequest = error.config;

  if (error.response.status === 401 && !originalRequest._retry) {

    originalRequest._retry = true;

    const refreshToken = window.localStorage.getItem('refreshToken');
    return axios.post('http://localhost:8000/auth/refresh', { refreshToken })
      .then(({data}) => {
        window.localStorage.setItem('token', data.token);
        window.localStorage.setItem('refreshToken', data.refreshToken);
        axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.token;
        originalRequest.headers['Authorization'] = 'Bearer ' + data.token;
        return axios(originalRequest);
      });
  }

  return Promise.reject(error);
});

Aquí, error.response devuelve undefined en la versión 0.14.0.

puedes probar error.status por favor

axios.interceptors.response.use(undefined, error => {
      console.log(error.status)
      return Promise.reject(error)
    })

El archivo console.log devuelve indefinido.

Estoy bastante seguro de que debería ser error.response.status , podría verificar el objeto de error si tiene esas propiedades. No sé por qué no funciona si solo tiene ese código de muestra aislado similar al que ha publicado anteriormente.

¿Este problema está resuelto? Estoy usando 0.15.2 y me encuentro con este extraño problema

@ dmt0 Lo mismo contigo ... Estoy usando 0.15.2 ... Entonces, ¿qué versión está bien para esto?

Usé el ejemplo de Vue.js 2.0 mi solución fue observar el token en busca de cambios en Vuex si cambiaba y luego actualizar cualquier cosa que cambiara. IE, si alguien quería darle Me gusta a una publicación pero el JWT venció, intercepta la solicitud y la ejecuta nuevamente una vez que el JWT se ha actualizado.

Vue.axios.interceptors.request.use((config) => {
    if (store.state.auth) {
      config.headers.common['Authorization'] = 'Bearer ' + store.state.token
    }
    return config
  }, function (error) {
    // Do something with request error
    return Promise.reject(error)
  })
Vue.axios.interceptors.response.use((response) => {
    return response
  }, function (error) {
    let originalRequest = error.config
    console.log(originalRequest)
    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true
      Vue.axios.post('/users/token', null).then((data) => {
        store.dispatch('authfalse')
        store.dispatch('authtruth', data.data)
        originalRequest.headers['Authorization'] = 'Bearer ' + store.state.token
        return Vue.axios(originalRequest)
      })
    }
    // Do something with response error
    return Promise.reject(error)
  })

¿Esto ya está resuelto? no hay response por error. da indefinido. Quiero verificar si el estado es 401 y luego redirigir al inicio de sesión.

En la función de rechazo del interceptor de respuesta, error.response.status se devuelve correctamente en la última versión (0.15.3).

¿Esto está resuelto? Estoy usando la versión 0.15.3 pero error.response es undefined . Intento manejar el error 401.
por cierto estoy usando "Instancia personalizada"

Pude adaptar el ejemplo de @rlambertsen . Funcionó bien.

Esto también es un problema para mí - v.0.15.3 - el error es solo una cadena de error de pila de llamadas

funciona aquí, estoy ejecutando axios v0.16.1.

Lo único que debe tener en cuenta es que si ha establecido baseURL en su configuración, debe eliminarlo del objeto error.config antes de volver a intentar la solicitud inicial.

this.responseInterceptor = this.axios.interceptors.response.use((response) => response, (error) => {
  let value = error.response;

  if (value.status === 401 && value.data.message === 'Expired JWT Token'
    && (!value.config || !value.config.renewToken)) {
    console.log('Token JWT expiré. Reconnexion ...');

    // renewToken performs authentication using username/password saved in sessionStorage/localStorage
    return this.renewToken().then(() => {
      error.config.baseURL = undefined; // baseURL is already present in url
      return this.axios.request(error.config);
    }).then((response) => {
      console.log('Reconnecté !');
      return response;
    });

  } else if (value.status === 401 && value.config && value.config.renewToken) {
    console.log('Echec de la reconnexion !');

    if (error.message) {
      error.message = 'Echec de la reconnexion ! ' + error.message + '.';
    } else {
      error.message = 'Echec de la reconnexion !';
    }

  } else if (value.status === 401) {
    console.log('Accès refusé.');
    // TODO: We could ask for username/password in a modal dialog...
  }

  return Promise.reject(error);
});

Estuve buscando una solución durante los últimos 3 o 4 meses en esto y todavía nada funcionó. Estoy usando Laravel para devolver el error y el navegador puede identificar el 401 (no autorizado) pero no ayuda con axios.
@Toilal Estoy intentando durante horas arreglar esto cada vez que veo un comentario que podría ayudar, pero no tuve suerte, hoy lo intenté de nuevo con "axios": "^0.16.1" sin suerte. ¿Alguien puede ayudar con esto? hay mucha gente que necesita ayuda con esto.

Las consolas de código a continuación Undefined . Simple, el error no tiene respuesta, no importa cómo lo intentes

axios.interceptors.response.use((response) => response, (error) => {
    let value = error.response
    console.log(value)
})

¿Puedes console.log(error) ? ¿Quizás es un error interno y falla antes de que se lea realmente una respuesta?

¿Puedes console.log(error) ? ¿Quizás es un error interno y falla antes de que se lea realmente una respuesta?

¿Quizás Laravel (o algo más) intercepte el error antes de axios y rechace la promesa con otro tipo de datos? Estoy usando axios en una aplicación VueJS, actualmente es una aplicación pequeña ya que es un nuevo proyecto.

¿Intenta averiguar si tiene otro interceptor axios funcionando antes que este?

Es una cuerda
console.log (error)

Error: Network Error
    at createError (eval at <anonymous> (app.js:1611), <anonymous>:15:15)
    at XMLHttpRequest.handleError (eval at <anonymous> (app.js:1590), <anonymous>:87:14)

Esto es cuando consuelo error.config
image

Sí, estoy usando axios con la aplicación Vuejs, así es como lo hice

/* global API_URL */
window.axios = require('axios').create({
  baseURL: API_URL,
  timeout: false,
  params: {} // do not remove this, its added to add params later in the config
})

// Add a request interceptor
/* global window axios */
axios.interceptors.request.use(function (config) {
  /* global window Store */
  let token = Store.get('jwt.token')
  let location = Store.get('location')

  // console.log(location.id, location)
  if (token) {
    config.headers.common['Authorization'] = 'Bearer ' + token
  }
  // Append location id for every post/get request
  if (location) {
    if (config.data) {
      config.data.location_id = location.id
    }
    else if (config.params) {
      config.params.location_id = location.id
    }
  }

  return config
}, function (error) {
  // Do something with request error
  return Promise.reject(error)
})

axios.interceptors.response.use((response) => response, (error) => {
    console.log(error.config)
})

@Toilal Descubrí el problema y no estoy seguro de cuál podría ser la solución. Si me quito
axios.interceptors.request part, funciona muy bien. ¿Deberíamos utilizar tanto la solicitud como la respuesta en la misma llamada? Si es así, ¿conoce una solución alternativa?

Yo uso ambos y funciona. ¿Devuelve los objetos correctos en su solicitud?
promesa del interceptor?

Le 11 avr. 2017 18:55, "vijay kumar" [email protected] a écrit:

@Toilal https://github.com/Toilal Descubrí el problema y no
seguro cuál podría ser la solución. Si me quito
axios.interceptors.request part, funciona muy bien. ¿Deberíamos usar ambos
solicitud y respuesta en la misma llamada? Si es así, ¿conoce una solución alternativa?

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/mzabriskie/axios/issues/266#issuecomment-293326920 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/ABHJvmxkHSGY0mEXmiygeUW7xViqiiMXks5ru7CGgaJpZM4Hwec2
.

Lo resolví, el 401 no tiene cors . Si alguien usa Laravel con el paquete Laravel Cors, aquí está la solución y tendrá error.response disponible

https://github.com/barryvdh/laravel-cors/issues/89

Tuve el problema, que error era undefined en un interceptor de respuesta y fue causado por no resolver la promesa de config en un interceptor de solicitud de actualización de token, si el token se actualiza fallido.

Código defectuoso en el interceptor de solicitudes:

axios.interceptors.request.use(config => this.dispatch('refreshAuthToken').then(() => {
  // do stuff to set Authorization header
  return Promise.resolve(config);
}))

Código fijo:

axios.interceptors.request.use(config => this.dispatch('refreshAuthToken').then(() => {
  // do stuff to set Authorization header
  return Promise.resolve(config);
// ensure we resolve config in error case
}), () => Promise.resolve(config));

Basado en esta muestra: https://github.com/axios/axios/issues/450#issuecomment -247446276

He usado esta versión para actualizar el token, como se describe en la publicación anterior. Este interceptor se crea para evitar que el token de actualización se repita si se crea más solicitud y es probable que desee llamar a esta operación solo una vez.

Esta es la versión mecanografiada, pero creo que es muy similar a la versión js.

export default class AuthorizationHelper {

    authTokenRequest: Promise<any>;

    getNewAccessToken() {

        const refreshToken = window.localStorage.getItem("refreshToken");

        if (!this.authTokenRequest) {
            this.authTokenRequest = this.refreshToken(refreshToken);
            this.authTokenRequest.then(response => {
                this.resetGetAccessTokenRequest();
            }).catch(error => {
                this.resetGetAccessTokenRequest();
            });
        }

        return this.authTokenRequest;
    }

    resetGetAccessTokenRequest() {
        this.authTokenRequest = null;
    }

    refreshToken(refreshToken: string): Promise<any> {

        return axios.post('/api/token/refresh',
            {
                refreshToken: refreshToken
            });
    }

    registerAxiosInterceptor() {
        axios.interceptors.response.use((response) => {
            return response;
        }, err => {
            const error = err.response;

            if (error.status === 401 && error.config && !error.config.__isRetryRequest) {

                return this.getNewAccessToken().then(response => {
                    error.config.__isRetryRequest = true;

                    //set new access token after refreshing it
                    axios.defaults.headers.common["Authorization"] = `Bearer ${response.access_token}`;
                    error.config.headers["Authorization"] = `Bearer ${response.access_token}`;

                    return axios(error.config);
                }).catch(error => {

                    //refreshing has failed => redirect to login
                    //clear cookie (with logout action) and return to identityserver to new login
                    //(window as any).location = "/account/logout";

                    return Promise.reject(error);
                });
            }

            return Promise.reject(error);
        });
    }
}
// for multiple requests
let isRefreshing = false;
let failedQueue = [];

const processQueue = (error, token = null) => {
  failedQueue.forEach(prom => {
    if (error) {
      prom.reject(error);
    } else {
      prom.resolve(token);
    }
  })

  failedQueue = [];
}

axios.interceptors.response.use(function (response) {
  return response;
}, function (error) {

  const originalRequest = error.config;

  if (error.response.status === 401 && !originalRequest._retry) {

      if (isRefreshing) {
        return new Promise(function(resolve, reject) {
          failedQueue.push({resolve, reject})
        }).then(token => {
          originalRequest.headers['Authorization'] = 'Bearer ' + token;
          return axios(originalRequest);
        }).catch(err => {
          return Promise.reject(err);
        })
      }

    originalRequest._retry = true;
    isRefreshing = true;

    const refreshToken = window.localStorage.getItem('refreshToken');
    return new Promise(function (resolve, reject) {
       axios.post('http://localhost:8000/auth/refresh', { refreshToken })
        .then(({data}) => {
            window.localStorage.setItem('token', data.token);
            window.localStorage.setItem('refreshToken', data.refreshToken);
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.token;
            originalRequest.headers['Authorization'] = 'Bearer ' + data.token;
            processQueue(null, data.token);
            resolve(axios(originalRequest));
        })
        .catch((err) => {
            processQueue(err, null);
            reject(err);
        })
        .then(() => { isRefreshing = false })
    })
  }

  return Promise.reject(error);
});

Proyecto de demostración
Enlace esencial

Lo que ve en Axios versión 0.13. * Respuesta de error la cadena devuelta por el método toString del objeto de error.

En las versiones más recientes, si se ha recibido una respuesta del servidor, el objeto de error contendrá la propiedad de respuesta:
Entonces puedes hacer algo como:

if (error.response) { console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); }

¿Fue útil esta página
0 / 5 - 0 calificaciones