'Axios: how to cancel request inside request interceptor properly?
I want to cancel the request if there's no token so I do like this:
instance.interceptors.request.use(config => {
if (!getToken()) {
console.log("interceptors: no access token");
} else {
config.headers.Authorization = "Bearer " + getToken().accessToken;
return config;
}
});
But in negative scenario there's an error TypeError: Cannot read property 'cancelToken' of undefined
.
Solution 1:[1]
You cannot use the token inside the interceptors but instead throw Cancel
axios.interceptors.response.use(function (response) {
throw new axios.Cancel('Operation canceled by the user.');
}, function (error) {
return Promise.reject(error);
});
Refer to this post: https://github.com/axios/axios/issues/583
Solution 2:[2]
For later googlers, this is a good solution taken from the axios issue on github
instance.interceptors.request.use(config => {
/* some logic */
return {
...config,
cancelToken: new CancelToken((cancel) => cancel('Cancel repeated request'))
};
});
This is pretty much what Vijay proposed but in a more concise form.
Solution 3:[3]
@Kirill Taletski's answer solve this perfectly, but add one line:
const CancelToken = Axios.CancelToken;
then ,it gonna be like this :
instance.interceptors.request.use(config => {
/* some logic */
const CancelToken = Axios.CancelToken;
return {
...config,
cancelToken: new CancelToken((cancel) => cancel('Cancel repeated request'))
};
});
Solution 4:[4]
here is the solution
import axios from 'axios';
const CancelToken = axios.CancelToken;
let cancel;
axios.interceptors.request.use((config) => {
if (cancel) {
cancel(); // cancel request
}
config.cancelToken = new CancelToken(function executor(c)
{
cancel = c;
})
return config
}, function (error) {
return Promise.reject(error)
});
Solution 5:[5]
I have implemented this in this way. I am not sure if this is the best solution, but for my use case is useful. My idea is not to cancel the last request. I would like to cancel previous requests to the same endpoint, and let the last one to do his job. For that reason I keep track of the request that are being executed.
// I keep track of the current requests that are being executed
const currentExecutingRequests = {};
axios.interceptors.request.use(
(req) => {
let originalRequest = req;
if (currentExecutingRequests[req.url]) {
const source = currentExecutingRequests[req.url];
delete currentExecutingRequests[req.url];
source.cancel();
}
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
originalRequest.cancelToken = source.token;
currentExecutingRequests[req.url] = source;
// here you could add the authorization header to the request
return originalRequest;
},
(err) => {
return Promise.reject(err);
}
);
axios.interceptors.response.use(
(response) => {
if (currentExecutingRequests[response.request.responseURL]) {
// here you clean the request
delete currentExecutingRequests[response.request.responseURL];
}
return response;
},
(error) => {
const { config, response } = error;
const originalRequest = config;
if (axios.isCancel(error)) {
// here you check if this is a cancelled request to drop it silently (without error)
return new Promise(() => {});
}
if (currentExecutingRequests[originalRequest.url]) {
// here you clean the request
delete currentExecutingRequests[originalRequest.url];
}
// here you could check expired token and refresh it if necessary
return Promise.reject(error);
}
);
Solution 6:[6]
As of Axios v0.22.0 an AbortSignal
is the recommended way to cancel from a request interceptor.
axios.interceptors.request.use(
(requestConfig) => {
/* some logic */
return {
...requestConfig,
signal: AbortSignal.abort()
};
}
},
(error) => {
return Promise.reject(error);
}
);
Solution 7:[7]
So for whatever reason none of these answers worked for me. Here is what did.
axiosInstance.interceptors.request.use(
function (config) {
const controller = new AbortController();
const cfg = {
...config,
signal: controller.signal,
};
controller.abort('We gotta cancel this');
return cfg;
},
function (error) {
return Promise.reject(error);
},
);
Thing I learned AbortController
is a native to javascript/node.
Solution 8:[8]
My solution based on https://stackoverflow.com/a/64228288/2051938
axios.ts
const axiosInstance = axios.create({ baseURL: apiBaseUrl });
axiosInstance.interceptors.request.use(
req => {
const originalRequest = req;
const cancelUniqId = (originalRequest.cancelToken as unknown) as string;
if (Object.hasOwnProperty.call(currentExecutingRequests, cancelUniqId)) {
const source = currentExecutingRequests[cancelUniqId];
delete currentExecutingRequests[cancelUniqId];
source.cancel();
}
if (cancelUniqId) {
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
originalRequest.cancelToken = source.token;
currentExecutingRequests[cancelUniqId] = source;
}
return originalRequest;
},
err => {
return Promise.reject(err);
}
);
axiosInstance.interceptors.response.use(
response => {
for (const key of Object.keys(currentExecutingRequests)) {
if (currentExecutingRequests[key].token === response.config.cancelToken) {
delete currentExecutingRequests[key];
break;
}
}
return response;
},
error => {
const { response } = error;
if (axios.isCancel(error)) {
return new Promise(() => {
//
});
}
for (const key of Object.keys(currentExecutingRequests)) {
if (currentExecutingRequests[key].token === response.config.cancelToken) {
delete currentExecutingRequests[key];
break;
}
}
return Promise.reject(error);
}
);
export { axiosInstance };
Usage:
axiosInstance.request({
url: "some/req/path",
method: "POST",
params: {...},
data: {...},
cancelToken: "someUniqRequestID" // <-- IMPORTANT!
})
as a result, all requests with someUniqRequestID
token will be cancelled when previous request with SAME cancelToken
was not finished before.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow