Reputation: 1016
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
.
Upvotes: 46
Views: 60946
Reputation: 628
v0.22.0
and higherAs per the documentation, cancellation is now pretty straightforward with the AbortController
class
instance.interceptors.request.use(config => {
/* some logic */
const controller = new AbortController();
if (needToCancelRequest) {
controller.abort();
}
return {
...config,
signal: controller.signal
};
});
You might be tempted to do a pretty concise signal: AbortSignal.abort()
instead. Please, note that it is much less supported than the solution above. See AbortSignal.abort()
vs new AbortController().abort()
compatibility.
v0.22.0
This is a solution taken from the axios issue on github
instance.interceptors.request.use(config => {
/* some logic */
return {
...config,
cancelToken: new CancelToken((cancel) => {
if (needToCancelRequest) {
cancel('Cancel repeated request')
}
})
};
});
Upvotes: 36
Reputation: 1747
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 from this: AbortController
is native to javascript/node.
Upvotes: 9
Reputation: 2010
This works for me for axios 0.20.0:
const interceptorRequestConfig = (config) => {
if (sourceRequest[config.url]) {
sourceRequest[config.url].cancel('Automatic cancellation')
}
const axiosSource = axios.CancelToken.source()
sourceRequest[config.url] = { cancel: axiosSource.cancel }
config.cancelToken = axiosSource.token
return config
}
credit for the idea: https://stackoverflow.com/a/66701130/8840359
Upvotes: 1
Reputation: 923
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);
}
);
Upvotes: 4
Reputation: 65
@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'))
};
});
Upvotes: 3
Reputation: 3935
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.
Upvotes: 1
Reputation: 511
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);
}
);
Upvotes: 5
Reputation: 500
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)
});
Upvotes: 2
Reputation: 774
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
Upvotes: 27