16
7

More than 3 years have passed since last update.

Spring Boot 2.2 から Content-Type: application/json に charset=UTF-8 が付かない

Last updated at Posted at 2020-06-16

概要

  • Spring Boot 2.2 から @RestController にて JSON を返す際の HTTP レスポンスヘッダで Content-Type: application/json になる (charset=UTF-8が付かない)
  • Spring Boot 2.1 では @RestController にて JSON を返す際の HTTP レスポンスヘッダで Content-Type: application/json;charset=UTF-8 になる

検証用ソースコード

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);
  }

  // Spring Boot 2.2 から Content-Type に charset=UTF-8 が付かない
  @GetMapping("/foo")
  public Map foo() {
    return Map.of("message", "こんにちわ、世界。");
  }

  // HTTP レスポンスヘッダを自前で指定することは可能
  @GetMapping("/bar")
  public ResponseEntity bar() {
    Map body = Map.of("message", "こんにちわ、世界。");
    // 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);
  }
}

Spring Boot 2.1 用 Gradle ビルドスクリプト

ファイル名 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'
}

Spring Boot 2.2 用 Gradle ビルドスクリプト

ファイル名 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'
}

検証

  • 実行環境: macOS Catalina + Java 11 (AdoptOpenJDK 11.0.7)

Spring Boot 2.1

Spring Boot 2.1 でビルドしてサーバを起動。

$ gradle -b 2.1-build.gradle bootRun

他のコンソールから curl でアクセス。
charset=UTF-8 が付いた Content-Type: application/json;charset=UTF-8 が HTTP レスポンスヘッダに入っている。

$ 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":"こんにちわ、世界。"}
$ 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":"こんにちわ、世界。"}

Spring Boot 2.2

Spring Boot 2.2 でビルドしてサーバを起動。

$ gradle -b 2.2-build.gradle bootRun

他のコンソールから curl でアクセス。
charset=UTF-8 が付かない Content-Type: application/json が HTTP レスポンスヘッダに入っている。

$ 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":"こんにちわ、世界。"}

自前で HTTP レスポンスヘッダに charset=UTF-8 を付けた場合は残っている。

$ 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":"こんにちわ、世界。"}

JSON の仕様

JSON は UTF-8 でエンコーディングされなければならない。

事実上最後のJSON仕様「RFC 8259」と「ECMA-404 2nd Editon」公開。UTF-8エンコード必須に - Publickey

RFC 8259でアップデートされたJSON仕様では、文字のエンコードとして「UTF-8」が必須となりました。

Content-Type に指定される MIME タイプ は application/json で charset は定義されていない。

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 は非推奨

Spring Boot 2.1 が依存している Spring Framework 5.1 の MediaType.APPLICATION_JSON_UTF8 では許容されていた。

MediaType (Spring Framework 5.1.16.RELEASE API) - Javadoc 日本語訳

public static final MediaType APPLICATION_JSON_UTF8
application/json;charset=UTF-8 のパブリック定数メディアタイプ。
この APPLICATION_JSON バリアントは、JSON コンテンツタイプの設定に使用する必要があります。RFC7159 では「この登録には文字セットパラメーターが定義されていません」と明記されていますが、ブラウザによっては UTF-8 特殊文字を正しく解釈する必要があるためです

Spring Boot 2.2 が依存している Spring Framework 5.2 から MediaType.APPLICATION_JSON_UTF8 は非推奨(@Deprecated)となっている。

MediaType (Spring Framework 5.2.7.RELEASE API) - Javadoc 日本語訳

APPLICATION_JSON_UTF8
使用すべきではありません。
Chrome のような主要なブラウザは仕様に準拠し、charset=UTF-8 パラメーターを必要とせずに UTF-8 の特殊文字を正しく解釈するため、5.2 の時点で APPLICATION_JSON を推奨しています。

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.

参考資料

16
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
7