[Java] Charset=UTF-8 is not added to Content-Type: application/json from Spring Boot 2.2

3 minute read

Overview

  • Content-Type: application/json in the HTTP response header when returning JSON with @RestController from Spring Boot 2.2 (charset=UTF-8 is not attached)
  • In Spring Boot 2.1, Content-Type: application/json;charset=UTF-8 in HTTP response header when returning JSON with @RestController

Source code for verification

src/main/java/com/example/MyApp.java

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.StandardCharsets;
import java.util.Map;

@SpringBootApplication
@RestController
public class MyApp {

  public static void main(String[] args) {
    SpringApplication.run(MyApp.class, args);
  }

  // Charset=UTF-8 is not added to Content-Type from Spring Boot 2.2
  @GetMapping("/foo")
  public Map foo() {
    return Map.of("message", "Hello world".);
  }

  // It is possible to specify the HTTP response header by yourself
  @GetMapping("/bar")
  public ResponseEntity bar() {
    Map body = Map.of("message", "Hello world".);
    // Specify Content-Type: application/json;charset=UTF-8
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, StandardCharsets.UTF_8));
    return new ResponseEntity<Map>(body, headers, HttpStatus.OK);
  }
}

Gradle build script for Spring Boot 2.1

Save it with the file name 2.1-build.gradle.

plugins {
  id'org.springframework.boot' version '2.1.15.RELEASE'
  id'io.spring.dependency-management' version '1.0.9.RELEASE'
  id'java'
}

group ='com.example'
version = '0.0.1'
sourceCompatibility = '11'

repositories {
  mavenCentral()
}

dependencies {
  implementation'org.springframework.boot:spring-boot-starter-web'
}

Gradle build script for Spring Boot 2.2

Save it with the file name 2.2-build.gradle.

plugins {
  id'org.springframework.boot' version '2.2.8.RELEASE'
  id'io.spring.dependency-management' version '1.0.9.RELEASE'
  id'java'
}

group ='com.example'
version = '0.0.1'
sourceCompatibility = '11'

repositories {
  mavenCentral()
}

dependencies {
  implementation'org.springframework.boot:spring-boot-starter-web'
}

Verification

  • Execution environment: macOS Catalina + Java 11 (AdoptOpenJDK 11.0.7)

Spring Boot 2.1

Build with Spring Boot 2.1 and start the server.

$ gradle -b 2.1-build.gradle bootRun

Access with curl from other consoles. Content-Type: application/json; charset=UTF-8 with charset=UTF-8 is included in the HTTP response header.

$ curl --include http://localhost:8080/foo
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 Jun 2020 12:15:23 GMT

{"message":"Hello, the world."}
$ curl --include http://localhost:8080/bar
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 Jun 2020 12:15:27 GMT

{"message":"Hello, the world."}

Spring Boot 2.2

Build with Spring Boot 2.2 and start the server.

$ gradle -b 2.2-build.gradle bootRun

Access with curl from other consoles. Content-Type: application/json without charset=UTF-8 is included in HTTP response header.

$ curl --include http://localhost:8080/foo
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Tue, 16 Jun 2020 12:16:24 GMT

{"message":"Hello, the world."}

If you add charset=UTF-8 to the HTTP response header by yourself, it remains.

$ curl --include http://localhost:8080/bar
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 Jun 2020 12:16:27 GMT

{"message":"Hello, the world."}

JSON specifications

JSON must be encoded in UTF-8.

Actually the final JSON specification “RFC 8259” and “ECMA-404 2nd Editon” released. UTF-8 encoding required-Publickey

In the JSON specification updated by RFC 8259, “UTF-8” is required as a character encoding.

The MIME type specified in Content-Type is application/json and charset is not defined.

RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format

The media type for JSON text is application/json.

Note: No “charset” parameter is defined for this registration. Adding one really has no effect on compliant recipients.

MediaType.APPLICATION_JSON_UTF8 is deprecated

It was allowed in MediaType.APPLICATION_JSON_UTF8 of Spring Framework 5.1 on which Spring Boot 2.1 depends.

MediaType (Spring Framework 5.1.16.RELEASE API) - Javadoc Japanese translation

public static final MediaType APPLICATION_JSON_UTF8 application/json;charset=UTF-8 public constant media type. This APPLICATION_JSON variant should be used to set the JSON content type. RFC7159 specifies “Charset parameter is not defined for this registration” because some browsers need to correctly interpret UTF-8 special characters.

MediaType.APPLICATION_JSON_UTF8 is deprecated (@Deprecated) from Spring Framework 5.2 which Spring Boot 2.2 depends on.MediaType (Spring Framework 5.2.7.RELEASE API) - Javadoc Japanese translation

APPLICATION_JSON_UTF8 Should not be used. As of 5.2, APPLICATION_JSON is recommended as major browsers like Chrome are compliant and correctly interpret UTF-8 special characters without requiring the charset=UTF-8 parameter.

Deprecate MediaType.APPLICATION_JSON_UTF8 in favor of APPLICATION_JSON · Issue #22788 · spring-projects/spring-framework · GitHub

  1. RFC 7159 has been obsoleted by RFC 8259
  2. That states “No “charset” parameter is defined for this registration. Adding one really has no effect on compliant recipients.” (at the end of Section 11)
  3. …and browsers have indeed fixed their code.

So it would be best to clarify the text and deprecate the constant.

Reference materials