Spring Boot + Kotlin でRESTアプリ作成ハンズオン3

前回↓

https://qiita.com/tseno/items/8009e985878c5b3f4355

次回↓

https://qiita.com/tseno/items/85507567505bb0a78a5b
# 3. GET、POST、PUT、DELETEの実装

## 3-1. Postmanのインストール

- ブラウザではPOST、PUT、DELETEメソッドを送信できないため、Postmanをインストールします。

https://www.postman.com/
より、Postmanをダウンロードして、インストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/0ebb04da-496d-c4d8-32a7-94a2915a5010.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/ab44380d-2a09-0ea3-5558-42cedd5be9cf.png)

`Postman-win64-Setup.exe` を起動するとインストール＆起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/24733b5c-11ca-6186-34ed-54a6d5544992.png)

- demoアプリケーションを起動して、GETメソッドで、`http://localhost:8080/todos` にアクセスすると、以下のようにJSONが返却されればOKです。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/55169b7e-36a5-3ced-f48a-23279966cdb3.png)


## 3-2. GETメソッドを追記＆ `ReesponseEntity` で返却する

### 3-2-1. TodoControllerクラスの修正

- 前回はTodoListすべてを取得する `@GetMapping("/todos")` を実装しましたが、今回は `id` を指定した1つのTodoを取得するメソッドを実装します。
- `@PathVariable` アノテーションは、URLの中の値(id)を取り出して、引数にセットします。
- findByIdの戻り値は、`Optional` 型なので、値があればの `map` メソッドによりResponseEntity.okに変換して返却します。
- 値が無ければ、 `orElse` メソッドにより、NotFoundを返却します。

```kotlin:TodoController.kt（以下を追記します）
    @GetMapping("/todo/{id}")
    fun getTodoById(@PathVariable("id") id: Long): ResponseEntity<Todo> {
        return todoRepository.findById(id).map { todo ->
            ResponseEntity.ok(todo)
        }.orElse(ResponseEntity(HttpStatus.NOT_FOUND))
    }
```

### 3-2-2. GETの動作確認

- POSTMANから、 http://localhost:8080/todo/1 を送信すると、以下のように出ればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/1f295458-312e-9fd0-f792-17aeae1c7b7b.png)

- http://localhost:8080/todo/100 のように存在しないidを指定すると、404 Not Found が出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/d5cc5829-6ae6-38df-ed26-96c487742342.png)

## 3-3. POSTメソッドの作成

### 3-3-1. Todoエンティティクラスの修正

- Todo.kt（エンティティクラス）のcreatedAt,updatedAtが自動的に入るようにするために、以下のように修正、追記します。

```kotlin:Todo.kt
package com.example.demo

import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate
import java.time.OffsetDateTime
import javax.persistence.*

@Entity
@Table(name = "todo")
class Todo(

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var id: Long? = null,

        @Column(name = "title")
        var title: String,

        @Column(name = "description")
        var description: String?,

        @Column(name = "is_completed")
        var isCompleted: Boolean,

        @Column(name = "created_at")
        @CreatedDate    // <- 追記
        var createdAt: OffsetDateTime?, // <- ?を付ける

        @Column(name = "updated_at")
        @LastModifiedDate   // <- 追記
        var updatedAt: OffsetDateTime? // <- ?を付ける
        
) {
    // 以下を追記
    
    @PrePersist
    fun prePersist() {
        createdAt = OffsetDateTime.now()
        updatedAt = OffsetDateTime.now()
    }

    @PreUpdate
    fun preUpdate() {
        updatedAt = OffsetDateTime.now()
    }
}
```

### 3-3-2. TodoControllerクラスの修正

- @RequestBodyアノテーションはJSONデータを対応するオブジェクトに変換します。
- letは、前（レシーバ）の結果を受け取ってラムダ式で処理します。
- 以下のメソッドを追記します。

```kotlin:TodoController.kt
    @PostMapping("/todo")
    fun postTodo(@RequestBody todo: Todo): ResponseEntity<Todo> {
        return todoRepository.save(todo).let {
            ResponseEntity.ok(it)
        }
    }
```

### 3-3-3. POSTの動作確認

- 以下のJSONをPOSTで http://localhost:8080/todo に送信します。

```json
{
  "title": "旅行の計画",
  "description": "夏休みに家族と海外旅行",
  "isCompleted": false
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/c065e2b0-76b2-0940-1288-0d00008f51a5.png)

- H2Console http://localhost:8080/h2-console/ でも入っていることが確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/d83e11df-9e53-b50a-e1c1-f9d9d86db1d7.png)


## 3-4. PUTメソッドの作成


### 3-4-1. TodoControllerクラスの修正

- findById()で取得したTodoがあれば、新しいTodoを作成して中身を更新したレスポンスを `200 OK` で返却し、なければ `404 NOT_FOUND` を返却します。

```kotlin:TodoController.kt（以下を追記します）
    @PutMapping("/todo/{id}")
    fun putTodoById(@PathVariable("id") id: Long,
                    @RequestBody todo: Todo): ResponseEntity<Todo> {
        return todoRepository.findById(id).map { oldTodo ->
            val newTodo = Todo(
                        id = id,
                        title = todo.title,
                        description = todo.description,
                        isCompleted = todo.isCompleted,
                        createdAt = oldTodo.createdAt,
                        updatedAt = oldTodo.updatedAt
                )
            ResponseEntity.ok().body(todoRepository.save(newTodo))
        }.orElse(ResponseEntity(HttpStatus.NOT_FOUND))
    }
```

### 3-4-2. PUTの動作確認

- 以下のJSONをPUTで http://localhost:8080/todo/1 に送信します。

```json
{
  "title": "歯医者の予約",
  "description": "歯医者の予約を取る",
  "isCompleted": false
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/bd105e2c-830a-11fc-31c2-f2adcd7b2358.png)

- H2Console http://localhost:8080/h2-console/ でも更新されていることが確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/2a7b43ca-94f9-5739-df85-6a8004dd4981.png)


## 3-5. DELETEメソッドの作成


### 3-5-1. TodoControllerクラスの修正

- findById()で取得したTodoがあればdelete()メソッドで削除して、中身を空にしたレスポンスを `200 OK` で返却し、なければ `404 NOT_FOUND` を返却します。

```kotlin:TodoController.kt（以下を追記します）
    @DeleteMapping("/todo/{id}")
    fun deleteTodoById(@PathVariable("id") id: Long): ResponseEntity<Unit> {
        return todoRepository.findById(id).map { todo ->
            todoRepository.delete(todo)
            ResponseEntity.ok().build<Unit>()
        }.orElse(ResponseEntity(HttpStatus.NOT_FOUND))
    }
```


### 3-5-2. DELETEの動作確認

- DELETEで http://localhost:8080/todo/1 に送信します。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/998aaae8-a131-6d23-9704-bdcbae672798.png)


- H2Console http://localhost:8080/h2-console/ でも削除されていることが確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/44a061c1-95a5-1c2b-2a51-7ec47603da10.png)

おわり