はじめに
OpenAPIを使うとサーバーとクライアント両方のコードが生成出来て便利です。
本記事では/api/csv
などのURLにアクセスするとCSVがダウンロード出来たり、/api/image
にアクセスすると画像が表示出来るようなAPIを作成する方法をご紹介します。
作ってみる
大まかな流れとしては以下のようになります。
- OpenAPI Spec Fileを作成
- openapi-generatorを使って、Spring Boot(Kotlin)プロジェクトを生成
- Serviceクラスを実装
-
./gradlew bootRun
で実行
CSVダウンロード
OpenAPI Spec Fileの作成
まずはSpec Fileを作成します。
今回はYAML形式で作成します。
http://localhost:8080/api/csv
にGETするとCSVがダウンロードされるAPIは以下のように定義します。
-
content
はtext/csv
にする -
scheme
のtype
はstring
にする
openapi: "3.0.2"
info:
title: API Title
version: "1.0"
servers:
- url: http://localhost:8080/api
tags:
- name: Download
description: ファイルダウンロード
paths:
/csv:
get:
summary: CSVダウンロード
tags:
- Download
responses:
"200":
description: OK
content:
text/csv:
schema:
type: string
Spring Boot(Kotlin)プロジェクトを作成
openapi-generatorがインストールされていない場合は、以下のコマンドでインストールします。
brew install openapi-generator
openapi-generator generate
コマンドを使用して、Spec FileからSpring Bootプロジェクトを生成します。
openapi-generator generate -i api.yml -g kotlin-spring --additional-properties=serviceInterface=true
簡単なオプションの説明は以下の通りです。
-
-i (---input-spec)
- インプットとなるSpec Fileのパスを指定します
-
-g (--generator-name)
- 何を生成するか出力します
- 他に使用できるものはこちらを参照してください
(Generators List · OpenAPI Generator)
-
--additional-properties
- generatorごとの設定を記述します
- serviceInterface=trueはService interfaceを作成して、Service classにロジックが書けるようにします
- kotlin-springで使用できるものはこちらを参照してください
(openapi-generator/kotlin-spring.md at master · OpenAPITools/openapi-generator)
Serviceクラスを実装
DownloadApiService
というInterfaceが自動生成されているので、それを継承したDownloadApiServiceImpl
を実装します。
戻り値がStringとなっているため、CSVとして出力したいStringを設定しておきます。
package org.openapitools.api
import org.openapitools.model.ImageType
interface DownloadApiService {
fun csvGet(): kotlin.String
}
package org.openapitools.api
import org.openapitools.model.ImageType
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.Resource
import org.springframework.stereotype.Service
@Service
class DownloadApiServiceImpl : DownloadApiService {
override fun csvGet(): String {
return "商品名,価格\nりんご,100\nオレンジ,200"
}
}
Spring Bootアプリケーションを実行
bootRunコマンドを実行して、APIにアクセスしてみます。
./gradlew bootRun
http://localhost:8080/api/image にアクセスするとCSVがダウンロードされました。
商品名,価格
りんご,100
オレンジ,200
Shift_JISで出力する
上記の方法だとUTF-8として出力されます。
Excelで簡単に取り込みたいとの要望から、Shift_JISで出力する場合を考えてみます。
http://localhost:8080/api/csv/sjis
というAPIを新設します。pathに以下を追加してください。
text/csv; charset=Shift_JIS
のように指定するのがポイントです。
path:
# 省略
/csv/sjis:
get:
summary: CSVダウンロード(sjis)
tags:
- Download
responses:
"200":
description: OK
content:
text/csv; charset=Shift_JIS:
schema:
type: string
あとは違いはないので、openapi-generatorで再度生成して上書きし、同様にServiceを実装します。
package org.openapitools.api
import org.openapitools.model.ImageType
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.Resource
import org.springframework.stereotype.Service
@Service
class DownloadApiServiceImpl : DownloadApiService {
override fun csvGet(): String {
return "商品名,価格\nりんご,100\nオレンジ,200"
}
override fun csvSjisGet(): String {
return "商品名,価格\nりんご,100\nオレンジ,200"
}
}
http://localhost:8080/api/csv/sjis にアクセスするとShift_JISエンコードのCSVが出力されます。
画像の表示
次は画像を表示するようなAPIを作成します。
Spec Fileに画像表示用のAPIを追加します。
-
content
はimage/jpeg
にする -
scheme
のtype
はstring
に、format
はbinary
にする
path:
# 省略
/image:
get:
summary: 画像ダウンロード
tags:
- Download
responses:
"200":
description: OK
content:
image/jpeg:
schema:
type: string
format: binary
openapi-generatorで再度生成すると、Resource
を返すようなメソッドが生成されています。
今回はsrc/main/resources/image.jpg
に置いた画像を表示するようにするため、ClassPathResource("image.jpg")
として指定します。
package org.openapitools.api
import org.openapitools.model.ImageType
interface DownloadApiService {
// 省略
fun imageGet(): org.springframework.core.io.Resource
}
package org.openapitools.api
import org.openapitools.model.ImageType
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.Resource
import org.springframework.stereotype.Service
@Service
class DownloadApiServiceImpl : DownloadApiService {
// 省略
override fun imageGet(): Resource {
return ClassPathResource("image.jpg")
}
}
http://localhost:8080/api/image にアクセスすると画像が表示されます。
MIME typeを切り替える
上記のAPIではMIME typeをimage/jpeg
として指定していました。
pngの画像も同じAPIで返したいとなったらどうしたら良いでしょうか?
そのままだとpngの画像もimage/jpeg
として返却されてしまいます。
content
には複数のMIME typeが設定できるので、複数設定するとSpring Boot側でいい感じに設定してもらえます。
query parameterでjpegかpngの画像を選択肢出来るようにしています。
path:
# 省略
/image/jpeg_or_png:
get:
summary: 画像ダウンロード(jpeg or png)
tags:
- Download
parameters:
- in: query
name: type
required: true
schema:
$ref: "#/components/schemas/ImageType"
responses:
"200":
description: OK
content:
image/jpeg:
schema:
type: string
format: binary
image/png:
schema:
type: string
format: binary
components:
schemas:
ImageType:
type: string
enum:
- jpeg
- png
あとは同様にServiceクラスを実装するだけです。
src/main/resources/image.jpg
にpngの画像を配置しておきます。
package org.openapitools.api
import org.openapitools.model.ImageType
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.Resource
import org.springframework.stereotype.Service
@Service
class DownloadApiServiceImpl : DownloadApiService {
// 省略
override fun imageGet(): Resource {
return ClassPathResource("image.jpg")
}
override fun imageJpegOrPngGet(type: ImageType): Resource {
return when (type) {
ImageType.jpeg -> ClassPathResource("image.jpg")
ImageType.png -> ClassPathResource("image.png")
}
}
}
http://localhost:8080/api/image/jpeg_or_png?type=jpeg にアクセスするとimage/jpeg
のjpeg画像が、
http://localhost:8080/api/image/jpeg_or_png?type=png にアクセスするとimage/png
のpng画像が表示されます。
おわりに
今回作成したサンプルは以下で確認できます。
GitHub
boronngo/openapi-spring-boot-file-download: File download API with openapi and spring boot
デモ
https://openapi-spring-boot-file-dow.herokuapp.com/api/csv
https://openapi-spring-boot-file-dow.herokuapp.com/api/csv/sjis
https://openapi-spring-boot-file-dow.herokuapp.com/api/image
https://openapi-spring-boot-file-dow.herokuapp.com/api/image/jpeg_or_png?type=jpeg
https://openapi-spring-boot-file-dow.herokuapp.com/api/image/jpeg_or_png?type=png