LoginSignup
2
3

More than 5 years have passed since last update.

Spring Boot for Kotlin 2

Last updated at Posted at 2017-10-29

開発準備

Spring Inirializrで以下のプロジェクトを作る。

Spring Initializr

  • ビルドツール: Gradle
  • 言語: Kotlin
  • Spring Bootのバージョン: 1.5.7
  • Group: com.example
  • Artifact: todolist
  • Dependencies: Web, Thymeleaf

タスクの一覧表示

タスクのデータクラスを作成

Task.kt
package com.example.todolist

data class Task(
        val id: Long,
        val content: String,
        val done: Boolean
)

コントローラーの作成

ダミータスクをプレーンテキストを返すコントローラー

TaskController.kt
package com.example.todolist

import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("tasks")
class TaskController {
    @GetMapping
    fun index(model: Model): String {
        val tasks = listOf(
                Task(1, "Test Task1", false),
                Task(2, "Test Task2", true),
                Task(3, "Test Task3", false)
        )
        return tasks.toString()
    }
}

@RequestMapping("tasks")の指定により、このクラスに定義するメソッドへのパスは"/tasks"から始まることを設定している。
@GetMapping("")の指定があるため、/tasksというパスへのGETリクエストに反応する。

動作確認

以下のコマンドでアプリケーションを起動。

./gradlew bootRun

以下のURLにブラウザにアクセス。

http://localhost:8080/tasks

ブラウザに以下の文字列が表示される。

[Task(id=1, content=Test Task1, done=false), Task(id=2, content=Test Task2, done=true), Task(id=3, content=Test Task3, done=false)]

タスク一覧をHTMLで返す

Controllerの修正

TaskController.kt
package com.example.todolist

import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping

@Controller
@RequestMapping("tasks")
class TaskController {
    @GetMapping
    fun index(model: Model): String {
        val tasks = listOf(
                Task(1, "Test Task1", false),
                Task(2, "Test Task2", true),
                Task(3, "Test Task3", false)
        )
        model.addAttribute("tasks", tasks)
        return "tasks/index"
    }
}

修正点

  • importとアノテーションをRestControllerからControllerに変更
  • model.addAttributeでテンプレートにデータを引き渡す処理を追加
  • returnはテンプレートの名称を返すように変更

テンプレートの追加

  • Thymeleafというテンプレートエンジンを使用
  • テンプレートファイルはsrc/main/resources/templates/に配置する
tasks/index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>タスク一覧</title>
</head>
<body>
<ul th:unless="${tasks.isEmpty()}">
    <li th:each="task: ${tasks}">
        <span th:unless="${task.done}" th:text="${task.content}"></span>
        <s th:if="${task.done}" th:text="${task.content}"></s>
    </li>
</ul>
</body>
</html>

  • Thymeleafのテンプレート要素はth:から始まる属性をつける
  • th:if は条件が当てはまるときに表示する
  • th:unless は th:if の逆
  • th:each は 要素の繰り返し
  • th:text は要素の中身を出力する

動作確認

ブラウザで以下のURLにアクセスする。

http://localhost:8080/task

ブラウザに以下が表示される

  • Test Task1
  • Test Task2
  • Test Task3

保存されているタスクを表示するように

TaskRepositoryインターフェースの定義

TaskRepository.kt
package com.example.todolist

interface TaskRepository {
    fun create(content: String): Task
    fun update(task: Task)
    fun findAll(): List<Task>
    fun findById(id: Long): Task?
}

メモリ上(変数)にタスクを保存するTaskRepositoryの実装をする。

InMemoryTaskRepository.kt
package com.example.todolist

import org.springframework.stereotype.Repository

@Repository
class InMemoryTaskRepository : TaskRepository {
    private val tasks: MutableList<Task> = mutableListOf()

    private val maxId: Long
        get() = tasks.map(Task::id).max() ?: 0

    override fun create(content: String): Task {
        val id = maxId + 1
        val task = Task(id, content, false)
        tasks += task
        return task
    }

    override fun update(task: Task) {
        tasks.replaceAll{ t ->
            if (t.id == task.id) task
            else t
        }
    }

    override fun findAll(): List<Task> = tasks.toList()

    override fun findById(id: Long): Task? = tasks.find { it.id == id }
}

上記を使うためにControllerの修正を行う。

TaskController.kt
package com.example.todolist

import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping

@Controller
@RequestMapping("tasks")
class TaskController(private val taskRepository: TaskRepository) {
    @GetMapping
    fun index(model: Model): String {
        val tasks = taskRepository.findAll()
        model.addAttribute("tasks", tasks)
        return "tasks/index"
    }
}

テンプレートを修正し、タスクがない旨を表す。

tasks/index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>タスク一覧</title>
</head>
<body>
<p th:if="${tasks.isEmpty()}">タスクがありません。</p>
<ul th:unless="${tasks.isEmpty()}">
    <li th:each="task: ${tasks}">
        <span th:unless="${task.done}" th:text="${task.content}"></span>
        <s th:if="${task.done}" th:text="${task.content}"></s>
    </li>
</ul>
</body>
</html>

※ pタグを追加

タスク新規作成

タスクを作成する機能を実装する。

フォームクラス作成

こういうクラスをフォームクラスと呼ぶらしい。
Symfonyのformhelperみたいなもんか?

TaskCreateFrom.kt
package com.example.todolist

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

class TaskCreateForm {
    @NotBlank
    @Size(max = 20)
    var content: String? = null
}

コントローラの修正

importの追加
```kotlin:TaskController.kt
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.servlet.mvc.support.RedirectAttributes
import org.springframework.validation.BindingResult
import org.springframework.validation.annotation.Validated


tasks/newページの作成

```kotlin:TaskController.kt
// 以下のメソッドを追加
@GetMapping("new")
fun new(form: TaskCreateForm): String {
    return "tasks/new"
}

タスクを新規作成する処理を追加

TaskController.kt
// 以下のメソッドを追加
@PostMapping("")
fun create(@Validated form: TaskCreateForm, bindingResult: BindingResult): String {
    if (bindingResult.hasErrors()) {
        return "tasks/new"
    }
    val content = requireNotNull(form.content)
    taskRepository.create(content)
    return "redirect:/tasks"
}

テンプレート追加/修正

tasks/new.htmlの追加

tasks/new.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>タスク作成</title>
</head>
<body>
<form th:method="post" th:action="@{./}" th:object="${taskCreateForm}">
    <div>
        <label>
            内容: <input type="text" th:field="*{content}" />
        </label>
        <p th:if="${#fields.hasErrors('content')}" th:errors="*{content}">エラーメッセージ</p>
    </div>
    <div>
        <input type="submit" value="作成" />
    </div>
</form>
</body>
</html>

tasks/index.htmlの修正

tasks/index.html
<!-- 次の行を追加 -->
<a th:href="@{tasks/new}">作成</a>

リンク

2
3
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
2
3