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

Default methods Content-Type headers cant be overridden #2623

Closed
Ashot-KR opened this issue Dec 25, 2019 · 12 comments
Closed

Default methods Content-Type headers cant be overridden #2623

Ashot-KR opened this issue Dec 25, 2019 · 12 comments

Comments

@Ashot-KR
Copy link

Describe the bug
When setting default Content-Type for methods (e.g. patch) always sends ContentType: application/json;charset=utf-8

To Reproduce

const api = axios.create({
  baseURL: '/api',
  headers: {
    common: {
      Accept: 'application/json'
    },
    patch: {
      'Content-Type': 'application/merge-patch+json'
    }
  }
})

api.patch('/user/1', { foo: 'bar' })

Expected behavior
Should send Content-Type: application/merge-patch+json header.

Environment:

  • Axios Version 0.19.0
  • OS: macOS 10.15.1
  • Browser Chrome, Safari
  • Browser Version 78.0.3904.108

Additional context/Screenshots
I think problem is here – root headers from config.headers always overrides headers for specific methods. In example above config.headers['Content-Type'] not set, so axios set it to application/json;charset=utf-8 under the hood and config.headers['Content-Type'] overrides config.headers.patch['Content-Type'].
I think methods headers should have higher priority and should be passed to utils.merge function in last argument

@Ashot-KR Ashot-KR changed the title Default methods Content-Type headers cant be overrided Default methods Content-Type headers cant be overridden Dec 25, 2019
@davidlandais
Copy link

davidlandais commented Jan 2, 2020

@Ashot-KR I've got the same problem. As a workaround, i'm using an interceptor.

axios.interceptors.request.use((config) => {
    /* using Typescript, method can be undefined */
    if(config.method){
      const headers = merge({}, config.headers.common, config.headers[ config.method ]);
      return merge({}, config, { headers });
    }
    return config;
  })

It is working for me. Hope this help.

EDIT: But axios provide their own defaults for post application/x-www-form-urlencoded. So, if you didn't defined defaults for this method and hope to use the commons this will fail.
I'm using Nuxt for a project with the nuxt-auth module. So i define my default headers in nuxt.config.js. Then I import the nuxt config in my interceptor and use it. At the moment, it is working for me.

$axios.interceptors.request.use((config) => {
    /* using Typescript, method can be undefined */
    if(config.method){
      const requestMethod = config.method.toLowerCase();
      const commonHeaders = get(nc.axios.headers, 'common', {});
      const methodHeaders = get(nc.axios.headers, requestMethod, {});

      const headers = merge({}, commonHeaders, methodHeaders);
      return merge({}, config, { headers });
    }
    return config;
  });

@pelepelin
Copy link

I've found a similar issue, i think it's the same, but my analysis is different.

Issue

When an object is passed in data and Axios default transformRequest is not overridden, the Content-Type configured for a method is not used, instead, the Content-Type is always application/json;charset=utf-8.

Reproduce

require('axios').create({
  headers: {
    post: {
      'Content-Type': 'application/json'
    }
  }
}).request({
  url: 'http://httpbin.org/anything',
  method: 'post',
  data: {}
}).then(response => console.log(response.data.headers['Content-Type']));

Result:

application/json;charset=utf-8

Reason

transformRequest is applied before merging headers:

config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
// Flatten headers
config.headers = utils.merge(
.

However, default transformRequest sets the Content-Type if it's not set:

axios/lib/defaults.js

Lines 50 to 51 in 351cf29

if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');

So, default by method Content-Type is not applied.

@pelepelin
Copy link

Workaround

Apply a custom transformRequest suitable for your usecase instead.

require('axios').create({
  transformRequest: (data, headers) => {
    if (data !== undefined) {
      headers['Content-Type'] = 'application/json';

      return JSON.stringify(data)
    } else {
      return data;
    }
  }
}).request({
  url: 'http://httpbin.org/anything',
  method: 'post',
  data: {}
}).then(response => console.log(response.data.headers['Content-Type']));

Result:

application/json

@pelepelin
Copy link

Workaround 2

Serialize data explicitly.

require('axios').create({
  headers: {
    post: {
      'Content-Type': 'application/json'
    }
  },
}).request({
  url: 'http://httpbin.org/anything',
  method: 'post',
  data: JSON.stringify({})
}).then(response => console.log(response.data.headers['Content-Type']));

Result:

application/json

@luisfuertes
Copy link

luisfuertes commented Jan 29, 2020

+1
Any update about this?

@chinesedfan chinesedfan linked a pull request Mar 21, 2020 that will close this issue
@chinesedfan chinesedfan moved this from To do to In progress in v0.20.0 Mar 24, 2020
v0.20.0 automation moved this from In progress to Done May 29, 2020
@jasonsaayman jasonsaayman reopened this May 29, 2020
v0.20.0 automation moved this from Done to In progress May 29, 2020
@santerref
Copy link

A solution that is working for me is to set the Content-Type header in the headers property instead of common:

axios.defaults.headers['Content-Type'] = 'application/vnd.api+json';

I think the issue is in dispatchRequest.js:

// Transform request data
config.data = transformData(
  config.data,
  config.headers,
  config.transformRequest
);

// Flatten headers
config.headers = utils.merge(
  config.headers.common || {},
  config.headers[config.method] || {},
  config.headers
);

The flatten headers should be done BEFORE the transform request data. Otherwise, axios set the Content-Type to application/json;utf-8 if the data sent is an object and after the merge cannot override it since it is set.

Also, the function setContentTypeIfUnset should have a third parameter to look into the current method of the request or have common as default value if not provided:

function setContentTypeIfUnset(headers, value) {
  if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {
    headers['Content-Type'] = value;
  }
}

Currently, it looks for headers['Content-Type'] and not headers.common['Content-Type'].

@mariukhin
Copy link

Just change the format of the sent data to JSON
axios can't recognize the data type of sended data, so it add 'charset=utf-8'

Use stringify
JSON.stringify(your data)

@WalderlanSena
Copy link

WalderlanSena commented Dec 18, 2020

is the last parameter for me is working, for example:

const response = api.patch, url + '/' + id, yourData, {
  headers: {
      'Accept': 'application/hal+json',
      'Content-Type': 'application/merge-patch+json'
  }
};

image

@pelepelin
Copy link

@WalderlanSena your example uses explicit headers settings for the request. This issue is about defaults provided to axios.create.

@jasonsaayman
Copy link
Member

I believe this should be amended in the latest version of Axios, please try it out and if the issue still exists, please open a new pull request.

@jraller
Copy link

jraller commented Nov 22, 2021

@jasonsaayman which version was this fixed in? I'm seeing it in 0.21.1 (the version at the time of your last comment) which is used by the nuxt-community axios-module and would like to know if upgrading that and nuxt auth to use 0.24 would allow me to set a default Content-Type header for my patch calls. I don't see this issue listed in the change log, or an entry that appears to match.

I looked in the tests and I'm not seeing test coverage for this case. Did I miss it?

@renevall
Copy link

In my case I had to do this:

this.instance.defaults.transformRequest = [(data, headers) => {
            if (data && headers) {
                if (headers["content-type"] === "application/json" || headers["Content-Type"] === "application/json") {
                    headers["Content-Type"] = "application/json"
                    return JSON.stringify(snakecaseKeys(data, { deep: true }))
                }
            }
        }]

I think there is an inconsistency going on, because response sees is as "content-type" and request as "Content-Type". If you try in your instance creation

this.instance = axios.create({
            baseURL,
            headers: { 'content-type': 'application/json' }

        });

Because of the merge process, then axios will send content-type: "application/json, application/json". Feel this is error prone.

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

Successfully merging a pull request may close this issue.