LoginSignup
47
48

More than 5 years have passed since last update.

Kotlin + Spring BootでREST APIを作ってみる

Last updated at Posted at 2017-05-21

はじめに

最近GoogleがAndroidで公式サポートすることを表明したのでやってみました。
乗るしかない、このビッグウェーブに。
実はただの備忘録です。

Spring Boot自体案件で使ったことがあっても、構築するのは初めてな上に、
Kotlinの本を読み切る前に手を動かしたため、かなり適当な感じが拭えないです。
指摘があれば是非お願いします。

あと、今回はSpring Initializrで作ったひな形を使ってます。

使用環境

  • macOS 10.12.5
  • IntelliJ Idea 2017.1.3
  • Java 1.8
  • Kotlin 1.1.2
  • Spring Boot 1.5.3 RELEASE

Spring Fox(SwaggerUI)の設定

動作確認にSwaggerUIを使いたいので以下のdependenciesをbuild.gradleに追記。

build.gradle
// Spring Fox/Swagger
compile "io.springfox:springfox-swagger2:2.6.0"
compile "io.springfox:springfox-swagger-ui:2.6.0"

あと、DemoApplication.ktと同じ階層にSwaggerConfiguration.ktを以下の内容で配置。

SwaggerConfiguration.kt
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import springfox.documentation.swagger2.annotations.EnableSwagger2
import springfox.documentation.builders.RequestHandlerSelectors
import springfox.documentation.spi.DocumentationType
import springfox.documentation.spring.web.plugins.Docket

@EnableSwagger2
@Configuration
class SwaggerConfiguration {

    @Bean
    fun customDocket(): Docket {
        return Docket(DocumentationType.SWAGGER_2).select()
                .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
                .build()
    }

}

Bodyの作成

今回はめんどくさいので受け取るのも返すのも同じPersonとします。

Person.kt

class Person(
        var name: String = "",
        var age: Int = 0
)

以下の記述だとJsonMappingExceptionでエラーになりました。
エラー読む限りだとデフォルトコンストラクタを拾えてないっぽいので、この書き方だとデフォルトコンストラクタ不可視になっちゃうんですかね。
どっかでそんな記述を見た記憶がありますが…。

Person.kt

// これだとJsonMappingExceptionが発生する。
class Person(
        var name: String,
        var age: Int
)

Controllerの作成

HelloController.kt
import com.example.demo.body.Person
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping(value = "hello")
class HelloController {

    @RequestMapping(value = "/person", method = arrayOf(RequestMethod.POST))
    fun getPerson(@RequestBody person: Person): Person {
        return Person(person.name, person.age)
    }

}

動作確認

GradleのbootRunタスクを叩いて起動できたら、SwaggerUIからリクエストを投げましょう。

結果

FireShot Capture 1 - Swagger UI_ - http___localhost_8080_swagger-ui.ht.jpg

TODO

  • @RangeとかValidate系のアノテーションのメンバへの適用
    • 書き方の問題っぽい
  • 省いたところもやってみる
    • SecurityとかDBアクセスとか

Spring Annotationsを使う

Stack Overflowに載ってました!
…あれ?なんかKotlinの思想的にはイマイチなのでは…?
とりあえずやったことは以下の通りです。

  1. メンバに@field:<任意のSpring Annotation>を付与する。
  2. 以下のコンストラクタをconstructorとして定義する。
    • パラメータなしのコンストラクタ(デフォルトコンストラクタの代替)
    • パラメータありのコンストラクタ
      ※任意。無くても動作します。

Bodyの修正

Person.kt

import org.hibernate.validator.constraints.Range
import javax.validation.constraints.NotBlank

class Person {
    @field:NotBlank
    var name: String

    @field:Range(min = 18)
    var age: Int

    // 💩💩💩コンストラクタ💩💩💩
    constructor(name: String, age: Int) {
        this.name = name
        this.age = age
    }

    // 💩💩💩デフォルトコンストラクタの代替💩💩💩
    constructor() {
        this.name = ""
        this.age = 0
    }
}

Controllerの修正

引数に@Validを加えるだけです。

HelloController.kt
import com.example.demo.body.Person
import org.springframework.web.bind.annotation.*
import javax.validation.Valid

@RestController
@RequestMapping(value = "hello")
class HelloController {

    @RequestMapping(value = "/person", method = arrayOf(RequestMethod.POST))
    fun getPerson(@Valid @RequestBody person: Person): Person {
        return Person(person.name, person.age)
    }
}

動作結果2

FireShot Capture 002 - Swagger UI_ - http___localhost_8080_swagger-ui.h.jpg

Response.json

{
  "timestamp": 1495557375614,
  "status": 400,
  "error": "Bad Request",
  "exception": "org.springframework.web.bind.MethodArgumentNotValidException",
  "errors": [
    {
      "codes": [
        "NotBlank.person.name",
        "NotBlank.name",
        "NotBlank.java.lang.String",
        "NotBlank"
      ],
      "arguments": [
        {
          "codes": [
            "person.name",
            "name"
          ],
          "arguments": null,
          "defaultMessage": "name",
          "code": "name"
        }
      ],
      "defaultMessage": "may not be empty",
      "objectName": "person",
      "field": "name",
      "rejectedValue": "",
      "bindingFailure": false,
      "code": "NotBlank"
    },
    {
      "codes": [
        "Range.person.age",
        "Range.age",
        "Range.int",
        "Range"
      ],
      "arguments": [
        {
          "codes": [
            "person.age",
            "age"
          ],
          "arguments": null,
          "defaultMessage": "age",
          "code": "age"
        },
        9223372036854776000,
        18
      ],
      "defaultMessage": "must be between 18 and 9223372036854775807",
      "objectName": "person",
      "field": "age",
      "rejectedValue": 17,
      "bindingFailure": false,
      "code": "Range"
    }
  ],
  "message": "Validation failed for object='person'. Error count: 2",
  "path": "/hello/person"
}

まとめ

Kotlinでも当然ながらSpringの基本機能は問題なく使えそうです。
ただ、アノテーション周りはいまいちな感じがするので、このあたりはもう少し調べていきたいですね。
あと残りのTODOも。

47
48
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
47
48