6

This is my axios configuration:

import axios from "axios"

const axiosApi = axios.create({
  baseURL: import.meta.env.VITE_API_URL
})

const requestInterceptor = config => {
  config.headers['Content-Type'] = 'application/json';
  config.headers['Accept'] = 'application/json';
  config.headers['X-Client'] = 'React';
  return config;
}

axiosApi.interceptors.request.use(requestInterceptor);

const get = async (url) => {
  return await
    axiosApi.get(url, {
      crossDomain: true
    }).then(response => {
      return response?.data;
    })
}

const post = async (url, data) => {
  return await axiosApi
    .post(url, Array.isArray(data) ? [...data] : { ...data })
    .then(response => response?.data)
}

const form = async (url, data) => {
  return await axiosApi
    .post(url, data, {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    })
    .then(response => response?.data)
}

As you can see, for post and get utility methods I use a request interceptor that sets the default values. Thus I use Content-Type: application/json for them.

However, for form I overrode the Content-Type header to be a form.

I read some other questions, including:

Axios not passing Content-Type header

Axios Header's Content-Type not set for safari

But my server allows Content-Type to be sent in CORS requests:

Access-Control-Allow-Headers: authorization,content-type,x-client
Access-Control-Allow-Methods: POST
Access-Control-Allow-Origin: *

But when I use form method, I see that the Content-Type is not set to application/json, not application/x-www-form-urlencoded.

What have I done wrong?

Big boy
  • 1,113
  • 2
  • 8
  • 23
  • 1
    Why mess with how Axios handles content-type headers by default? – Phil Jul 18 '22 at 05:22
  • Mess? It's all from its docs. We use interceptors and they are from docs. – Big boy Jul 18 '22 at 05:36
  • 1
    You don't need the optional chaining in `response?.data`. If the request resolves, `response` is guaranteed to be an [Axios response](https://axios-http.com/docs/res_schema) instance – Phil Jul 18 '22 at 06:11

2 Answers2

7

By default Axios has excellent request body handling.

  • If it sees a plain JavaScript object or array, it uses application/json.
  • If you pass in a plain string or an instance of URLSearchParams, it uses application/x-www-form-urlencoded.
  • Pass in a FormData instance and it will use multipart/form-data

So why are there endless questions on Stack Overflow with customised content-type headers? I'd even go so far as to argue that unless your APIs are using proper content negotiation, you don't need to mess with the Accept header either.

I see no need for an interceptor in your case. Simply set the request header defaults on your instance

axiosApi.defaults.headers.common["X-Client"] = "React";
// and if your API actually uses content negotiation...
// axiosApi.defaults.headers.common.Accept = "application/json";

As for your url-encoded request, Axios 0.x supports this via URLSearchParams or a plain string. It will not automatically convert a plain object into application/x-www-form-urlencoded.

If your data is a flat object, you can use the following

const form = async (url, data) => {
  const encodedData = new URLSearchParams(data);
  return (await axiosApi.post(url, encodedData)).data;
};

If it's more complex, I would recommend using a library like qs.

Otherwise, wait for Axios 1.0 where you can apparently use this

axios.post(url, data, {
  headers: { "content-type": "application/x-www-form-urlencoded" }
});
Phil
  • 157,677
  • 23
  • 242
  • 245
0

I was struggling for uploading media to the server. As my global Axios instance has Content-Type 'application/json' and then when the request was made I was updating the Content-Type to 'multipart/form-data' using below script.

// Not Working
this.axiosInstance.defaults.headers.common['Content-Type'] = contentType;

which was still not updated as the request header in the network tab still contains 'application/json' from global configurations (This could be the possible reason - the global header is saved in some other reference and we are updating it in some other reference)

So the fix is to intercept the request just before it flies and then modify the header as shown below

// Working
this.axiosInstance.interceptors.request.use(config => {
    config.headers['Content-Type'] = contentType;
    return config;
});

With Axios 1.X.X - Updating headers via 3rd argument is also working

this.axiosInstance.post(url, payload, {
  headers: { "Content-Type": contentType }
});

Hope this will help you or somebody else. Thanks!

Happy Coding :-)

Aman Kumar Gupta
  • 2,640
  • 20
  • 18