I first encountered Stack Overflow question no. 79616625, reporting duplicate Content-Type headers in REST docs, specifically in request docs.

A reproducer can be found here at https://github.com/Jeffrey-Oh/restassured-restdocs-issue.

The culprit seems to be method MockHttpServletRequest::addHeader. Its current implementation has a problem:

public void addHeader(String name, Object value) {
  if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name) &&
    !this.headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
    setContentType(value.toString());
  }
  else if (HttpHeaders.ACCEPT_LANGUAGE.equalsIgnoreCase(name) &&
    !this.headers.containsKey(HttpHeaders.ACCEPT_LANGUAGE)) {
    try {
      HttpHeaders headers = new HttpHeaders();
      headers.add(HttpHeaders.ACCEPT_LANGUAGE, value.toString());
      List<Locale> locales = headers.getAcceptLanguageAsLocales();
      this.locales.clear();
      this.locales.addAll(locales);
      if (this.locales.isEmpty()) {
        this.locales.add(Locale.ENGLISH);
      }
    }
    catch (IllegalArgumentException ex) {
      // Invalid Accept-Language format -> just store plain header
    }
    doAddHeaderValue(name, value, true);
  }
  else {
    doAddHeaderValue(name, value, false);
  }
}

Please note, how a Content-Type header is only set if it does not exist yet. If it already exists and is set again, at the end of the method we fall through to doAddHeaderValue(name, value, false), adding a second value to the already existing header. Technically, the underlying data structure is a map with the header name being the key and a HeaderValueHolder being able to hold multiple values. For Content-Type, however, only a single value is allowed, and there also should not be multiple instances of this header. By the way, the value false in doAddHeaderValue(name, value, false) means to not replace but amend the value list.

I have not checked the logic for the similar case Accept-Language, but AFAIK there multiple values are allowed - not sure about multiple occurrences of the header itself though.

Please, fix the logic for Content-Type.

CC @Jeffrey-Oh

Comment From: kriegaex

A simple change would be to always set (i.e. overwrite, not add) the Content-Type header (untested change):

  if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
    setContentType(value.toString());
  }

I also forgot to link to some REST docs with duplicate headers:

  • https://github.com/Jeffrey-Oh/restassured-restdocs-issue/blob/master/build/generated-snippets/user/logout/http-request.adoc
  • https://github.com/Jeffrey-Oh/restassured-restdocs-issue/blob/master/build/generated-snippets/user/logout/httpie-request.adoc
  • https://github.com/Jeffrey-Oh/restassured-restdocs-issue/blob/master/build/generated-snippets/user/logout/curl-request.adoc