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) }
)
}
})
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
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
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);
});
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);
}
Comentario más útil
Podrías hacer algo como esto: