Spring BootのMediaTypeと対応クラスの決まり方
概要
Kotlinに不慣れだったため、Spring BootのRouterFunctionでjpegイメージをByteの配列で返す際、KotlinのArray<Byte>とByteArray
の違いで上手くいかなかった。
それを調べている際にSpring BootのMediaTypeと対応クラスの決まり方について少し調べたため、備忘録として記事に残しておく。
冒頭の課題の結論としては画像を返したければByteArrayを使えば良く、Array<Byte>ではダメな理由は対応するEncoderが無いためだと思われる (ここでタイトルが関わってくる)。
例
fooService.getFoo()はArray<Byte>もしくはByteArrayで画像データを返すメソッドとする。
失敗
@Component
class FooHandler(private val fooService: FooService) {
fun getFoo(req: ServerRequest): Mono<ServerResponse> {
return ok()
.contentType(MediaType.IMAGE_JPEG)
.body(
fooService.getFoo(),
Array<Byte>::class.java
)
}
}
org.springframework.web.reactive.function.UnsupportedMediaTypeExceptionがthrowされて失敗する。
レスポンスとしては500エラーで
Content type 'image/jpeg' not supported for bodyType=java.lang.Byte[]
と言われる。
成功
@Component
class FooHandler(private val fooService: FooService) {
fun getFoo(req: ServerRequest): Mono<ServerResponse> {
return ok()
.contentType(MediaType.IMAGE_JPEG)
.body(
fooService.getFoo(),
ByteArray::class.java // ←ここ
)
}
}
理由
KotlinのArrayとJavaの配列
上記リンクによれば、以下のような対応になっているとのこと。
| Kotlin | Java |
|---|---|
Array<Byte> |
Byte[] |
ByteArray |
byte[] |
パフォーマンスを目的にJavaのプリミティブ型にはそれぞれ特化したクラスが用意されているらしい。
Spring BootのMediaTypeと対応クラス
そもそもByte[]で何故使えないのかが気になった。
これが使われているコードを見てみると、
このメソッドによって対応するMediaTypeが決まっていた。
Byte[]が対応するMediaTypeをIntelliJのdebuggerで調べてみると以下の四つだった。
application/jsonapplication/*+jsonapplication/x-ndjsontext/event-stream
これは
このあたりで、用意されているEncoderクラスを回してencodeできるMediaTypeのみを抜き出すことで決まっている。
例えばEncoderの一つであるJackson2JsonEncoderを見ると、親クラスであるJackson2CodecSupportで以下のようにサポートしているMediaType
が定義されており、
これとcanEncode()の実装で対応MediaTypeか否かが決まる。
具体的には、
- 指定
MediaTypeがサポートに入っていて、 - 指定
MediaTypeと指定classに対応するObjectMapperが登録されていて、 - その
ObjectMapperでserializeできる
場合にMediaTypeが対応しているかが決まるようである。
まとめ
画像を返したいならArray<Byte>ではなくByteArray を使えば良い。
理由はKotlinのArray<Byte>がJavaのByte[]に対応しており、Spring Bootで定義されるEncoderがByte[]に対応していないから。
その他
ここFooWriterクラスを回しているのにreaderという変数名になってることに気づいた。
Discussion