I'm using the library axios in my react app.
I'm having a problem with the interceptor.
My question is let say I have three requests happening concurrently and I don't have the token, the interceptor calling the getUserRandomToken
three time, I want the interceptor will wait until I'm getting the token from the first request and then continue to the others.
P.S. the token he is with an expiration date so I also checking for it and if the expiration date is not valid I need to create a new token.
This is the interceptor:
axios.interceptors.request.use(
config => {
/*I'm getting the token from the local storage
If there is any add it to the header for each request*/
if (tokenExist()) {
config.headersmon["token"] = "...";
return config;
}
/*If there is no token i need to generate it
every time create a random token, this is a axios get request*/
getUserRandomToken()
.then(res => {
/*add the token to the header*/
config.headersmon["token"] = res;
return config;
})
.catch(err => {
console.log(err);
});
},
function(error) {
// Do something with request error
return Promise.reject(error);
}
);
I'm using the library axios in my react app.
I'm having a problem with the interceptor.
My question is let say I have three requests happening concurrently and I don't have the token, the interceptor calling the getUserRandomToken
three time, I want the interceptor will wait until I'm getting the token from the first request and then continue to the others.
P.S. the token he is with an expiration date so I also checking for it and if the expiration date is not valid I need to create a new token.
This is the interceptor:
axios.interceptors.request.use(
config => {
/*I'm getting the token from the local storage
If there is any add it to the header for each request*/
if (tokenExist()) {
config.headers.mon["token"] = "...";
return config;
}
/*If there is no token i need to generate it
every time create a random token, this is a axios get request*/
getUserRandomToken()
.then(res => {
/*add the token to the header*/
config.headers.mon["token"] = res;
return config;
})
.catch(err => {
console.log(err);
});
},
function(error) {
// Do something with request error
return Promise.reject(error);
}
);
Share
Improve this question
edited Jan 17, 2019 at 18:41
Anas Abu Farraj
1,5984 gold badges23 silver badges32 bronze badges
asked Jan 17, 2019 at 17:16
Ron ShoshaniRon Shoshani
1,2131 gold badge10 silver badges13 bronze badges
3
- you can create a flag in some global context (localstorage) of which will hold a state of "generating token" and use that to tell the other request to wait until its done – Ori Price Commented Jan 17, 2019 at 17:33
- you can also create a singleton object that will handle the token generations. all calls will ask the same instance for the token. that singleton will be responsible for genereting new token for first time and return it in furthur requests – Ori Price Commented Jan 17, 2019 at 17:39
- @OriPrice I tried to do it with a flag but unfortunately, it didn't go well because the request for the token only happened once but the other request in the interceptor did not stop so I already got response 401(unauthorized) from them. – Ron Shoshani Commented Jan 17, 2019 at 17:59
3 Answers
Reset to default 1How about singleton object that will handle the token generations? something similar to this:
const tokenGenerator ={
getTokenPromise: null,
token: null,
getToken(){
if (!this.getTokenPromise){
this.getTokenPromise = new Promise(resolve=>{
/*supposed to be a http request*/
if (!this.token){
setTimeout(()=>{
this.token = 'generated';
resolve(this.token);
},0)
}else{
resolve(this.token);
}
})
}
return this.getTokenPromise;
}
you can reference this same object from the interceptors.
see example: JS FIddle reference: reference
You can return a Promise from interceptor callback to "wait" until promise fullfiles (this will fit your case). Check out this example:
function axiosCall () {
return new Promise((resolve, reject) => {
Axios.post(URL, {apiKey}).then((response) => {
resolve(response.data.message);
}).catch((error) => {
reject(error);
});
});
}
instance.interceptors.request.use((config) => {
return axiosCall().then((tokenResponse) => {
setWebCreds(tokenResponse);
config.headers.Authorization = `Bearer ${tokenResponse}`;
return Promise.resolve(config)
}).catch(error => {
// decide what to do if you can't get your token
})
}, (error) => {
return Promise.reject(error);
});
More details here: https://github./axios/axios/issues/754
Following code doing certain tasks:
- Update Token on 401
- Make a queue of failed requests while the token is refreshing.
- Restore the original request after token refreshing.
- Once the peculiar request is given 200, remove it from the queue.
Config.js
import axios from 'axios'; import { AsyncStorage } from 'react-native'; import { stateFunctions } from '../../src/sharedponent/static'; const APIKit = axios.create({ baseURL: '', timeout: 10000, withCredentials: true, }); const requestArray = []; // Interceptor for Request export const setClientToken = token => { APIKit.interceptors.request.use( async config => { console.log('Interceptor calling'); let userToken = await AsyncStorage.getItem('userToken'); userToken = JSON.parse(userToken); config.headers = { 'Authorization': `Bearer ${userToken}`, 'Accept': 'application/json', "Content-Type": "application/json", "Cache-Control": "no-cache", } // console.log('caling ' , config) return config; }, error => { Promise.reject(error) }); }; // Interceptor for Response APIKit.interceptors.response.use( function (response) { if (requestArray.length != 0) { requestArray.forEach(function (x, i) { if (response.config.url == x.url) { requestArray.splice(i, 1); } }); } return response; }, function (error) { const originalRequest = error.config; requestArray.push(originalRequest); let reqData = "username=" + number + "&password=" + pin + "&grant_type=password" + "&AppType=2" + "&FcmToken=null"; // console.log('error ' , error); if (error.message === "Request failed with status code 401" || error.statuscode === 401) { if (!originalRequest._retry) { originalRequest._retry = true; return axios({ method: 'post', url: '/api/login', data: reqData, headers: { "Content-Type": "application/x-www-form-urlencoded", "Cache-Control": "no-cache", } }) .then(res => { let response = res.data; console.log('successfull Login', response) if (res.data.StatusCode == 200) { AsyncStorage.setItem('userToken', JSON.stringify(response.access_token)); stateFunctions.UserId = response.UserId; stateFunctions.CustomerContactID = response.CustomerContactID; let obj = { access_token: response.access_token, token_type: response.token_type, expires_in: response.expires_in, UserId: response.UserId, CustomerContactID: response.CustomerContactID, Mobile: response.Mobile, StatusCode: response.StatusCode } AsyncStorage.setItem('logindetail', JSON.stringify(obj)); if (requestArray.length != 0) { requestArray.forEach(x => { try { console.log(x, "request Url"); x.headers.Authorization = `Bearer ${response.access_token}`; x.headers["Content-Type"] = "application/x-www-form-urlencoded"; APIKit.defaults.headers.mon["Authorization"] = `Bearer${response.access_token}`; APIKit(x) } catch (e) { console.log(e) } }); } return APIKit(originalRequest); } }) .catch(err => { console.log(err); }); } } return Promise.reject(error); } ); export default APIKit;
Home.js
gettingToken = async () => { let userToken = await AsyncStorage.getItem('userToken'); userToken = JSON.parse(userToken); await setClientToken(userToken); }