TotalAMD
TotalAMD

Reputation: 1016

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.

Upvotes: 46

Views: 60946

Answers (9)

Kirill Taletski
Kirill Taletski

Reputation: 628

Axios v0.22.0 and higher

As 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
  };
});

Browser Compatibility

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.

Axios before 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

Thomas Valadez
Thomas Valadez

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

Jar
Jar

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

MarkWPiper
MarkWPiper

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

jaycethanks
jaycethanks

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

mixalbl4
mixalbl4

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

Luciano Corniglione
Luciano Corniglione

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

Vijay sadhu
Vijay sadhu

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

grane2212
grane2212

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

Related Questions