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 // ←ここ
)
}
}
理由
Array
とJavaの配列
Kotlinの
上記リンクによれば、以下のような対応になっているとのこと。
Kotlin | Java |
---|---|
Array<Byte> |
Byte[] |
ByteArray |
byte[] |
パフォーマンスを目的にJavaのプリミティブ型にはそれぞれ特化したクラスが用意されているらしい。
MediaType
と対応クラス
Spring BootのそもそもByte[]
で何故使えないのかが気になった。
これが使われているコードを見てみると、
このメソッドによって対応するMediaType
が決まっていた。
Byte[]
が対応するMediaType
をIntelliJのdebuggerで調べてみると以下の四つだった。
application/json
application/*+json
application/x-ndjson
text/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