Spring Boot2系からStringをRestControllerで返している場合にデフォルトのContent-Typeが変わってしまった話
変わってしまった。 これは特殊な条件下によって起きる。
どのようなコードだったか
@RestController public class MyController { @GetMapping public ResponseEntity<String> get() { return ResponseEntity.ok("Hello World"); } }
どうなったか
手元のSpring Boot1系ではRestControllerでStringを返した場合にJSONのContent-Typeで "Hello World"
といった形で
quote付きでbodyに書き込まれていた。
Spring Boot2系にしたところ、Hello World
といった形でquoteなしになってしまった
どういうケースで発生するか
条件としては以下.
- Spring BootにあるWebMvcのAutoConfigurationを使っている
- 大体は自分でEnableWebMvcを使っていないケース
- 以下のような形でWebMvcConfigurerの実装でMessageConverterを追加している
@Configuration public class WebMvcConfiguration implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new MappingJackson2HttpMessageConverter()); } }
影響と対処法
JSONとして処理している箇所の修正が発生する。
対処法1: Acceptヘッダをつける
クライアント側のコードを修正して、Acceptヘッダでapplication/jsonなどを指定すればちゃんと思った通りの動作になる。
対処法2: クソイディオムを使う
これはHttpMessageConvertersでも使われている デフォルトのMessageConverterを取得する処理を追加して 自分たちのMessageConverterが優先されるようにしています。 いつの日か動かなくなる可能性があります。
@Configuration public class WebMvcConfiguration implements WebMvcConfigurer { - @Override - public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { - converters.add(new MappingJackson2HttpMessageConverter()); - } + @Bean + public HttpMessageConverters httpMessageConverters() { + return new HttpMessageConverters(/* add default converter */ false, Collections.singletonList(new MappingJackson2HttpMessageConverter())); + } + @Override + public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { + converters.addAll(new WebMvcConfigurationSupport() { + public List<HttpMessageConverter<?>> defaultMessageConverters() { + return super.getMessageConverters(); + } + }.defaultMessageConverters()); + } }
対処法3: そもそもStringを返さない
多分これが一番ラクな気がする。 エンドポイント数が多いと死亡。
jacksonのTextNodeで返すと良さそう。
対処法4: Spring BootのWebMvcを使わない
あんまりやりたくない
まとめ
今回の例は非常にエッジケースの問題ですが そもそもStringをJSONとして返さないでも良くない?って話はあると思います。
詳しく調べていないが、初期化順序が変わった模様?