Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to add a retry method to axios? #934

Closed
liuzhen2008 opened this issue Jun 2, 2017 · 12 comments
Closed

Is it possible to add a retry method to axios? #934

liuzhen2008 opened this issue Jun 2, 2017 · 12 comments

Comments

@liuzhen2008
Copy link

In my interceptors, I wish to catch a specific error (say, token expired)

And then I'll run some method to refresh the token.

After that resolves, I wish axios to retry the previous request (with the same result handler).

Do I need to implement this logic manually? Or is there a retry or something similar that allows me to do it directly?

Thanks

@rubennorte
Copy link
Member

You can do that with an interceptor that handles authentication errors. Something like:

axios.interceptors.response.use(null, (error) => {
  if (error.config && error.response && error.response.status === 401) {
    return updateToken().then((token) => {
      error.config.headers.xxxx <= set the token
      return axios.request(config);
    });
  }

  return Promise.reject(error);
});

@mmsmsy
Copy link

mmsmsy commented Dec 7, 2017

When using it I get "config is undefined". Where should I get it from?

@mmieluch
Copy link

@mmsmsy This line:

return axios.request(config);

should be:

return axios.request(error.config);

@skyrpex
Copy link

skyrpex commented Feb 16, 2018

Be careful if you're using the transformRequest option: the data from error.config is already transformed into a string.

For example, the code from @rubennorte would crash with this setup:

// Lets use qs to stringify our request.
this.$axios = axios.create({
  transformRequest: [
    data => qs.stringify(data),
  ],
});

this.$axios.interceptors.response.use(null, (error) => {
  if (error.config && error.response && error.response.status === 401) {
    return updateToken().then((token) => {
      error.config.headers.xxxx <= set the token
      // Here, the request data will be double stringified with qs.stringify,
      // potentially leading to 422 responses or similar.
      return this.$axios.request(config);
    });
  }

  return Promise.reject(error);
});

A solution to this problem is not to transform the request data if it's already a string. For example:

this.$axios = axios.create({
  transformRequest: [
    data => (isString(data) ? data : qs.stringify(data)),
  ],
});

@remmycat
Copy link

remmycat commented Apr 3, 2018

@skyrpex Thanks a lot! Your answer brought us on the right track on a long and complicated bug hunt!

It seems the default transformRequest handles transformation like in your example, but axios notices the data is a string and will set the content type to text/plain instead of application/json which lead in our case to our API sending back a 400 error.

This was a hard bug to catch!

@NomiasSR
Copy link

remhume can you give me a hand? I'm having an issue with the same wrong behaviour that you had on your program.

@remmycat
Copy link

iirc we just manually set the content type to the correct one (in our case application/json), which solved it.

@NomiasSR
Copy link

NomiasSR commented Jul 28, 2018 via email

@akhrabrov
Copy link

akhrabrov commented Apr 4, 2019

You can do that with an interceptor that handles authentication errors. Something like:

axios.interceptors.response.use(null, (error) => {
  if (error.config && error.response && error.response.status === 401) {
    return updateToken().then((token) => {
      error.config.headers.xxxx <= set the token
      return axios.request(config);
    });
  }

  return Promise.reject(error);
});

If i have 10 parralel requests, axios send 10 updateToken requests.
That may cause "ECONNRESET" error.
Maybe u have solution for this case?

P.S find solution here https://gist.github.com/mkjiau/650013a99c341c9f23ca00ccb213db1c

@Flyrell
Copy link

Flyrell commented Apr 6, 2019

@akhrabrov I made a simple package if you'd like to use it -> axios-auth-refresh. I'd be more than happy to see the contributions if you're missing some of the functionalities.

@edmondburnett
Copy link

edmondburnett commented Jul 25, 2019

I wanted to do something similar, except not necessarily with authentication, and to keep retrying after a delay when a particular status code is received. Essentially polling via an Axios interceptor. This was my solution:

import axios from 'axios';

const instance = axios.create({
    baseURL: 'http://localhost:5000'
});

const sleepRequest = (milliseconds, originalRequest) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(instance(originalRequest)), milliseconds);
    });
};

instance.interceptors.response.use(response => {
    return response;
}, error => {
    const { config, response: { status }} = error;
    const originalRequest = config;

    if (status === 420) {
        return sleepRequest(1000, originalRequest);
    } else {
        return Promise.reject(error);
    }
});

export default instance;

https://gist.github.com/edmondburnett/38ed3451de659dc43fa3f24befc0073b

@mortezashojaei
Copy link

You should also use below code for prevent crash in post requests :
if(error.config.data) error.config.data = JSON.parse(error.config.data)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests