Httpリクエストで値を渡す方法（Spring Boot）

# この記事は
`Spring Boot`を利用したRestful APIサーバに対するリクエスト方法3パターンを書きます。
1. リクエストボディのJson
1. URIパラメータ（`api/user/{userId}`のようなURI）
1. クエリストリング（`api/user?name="hoge"`のようなURI）

の３つです。

# リクエストボディ
`@RequestBody`を使用します。

## 基本
以下のように、コントローラのメソッドの引数に`@RequestBody`を付与します。引数の型には実際に受け取るJsonの型を持つクラスなどを作成しておくと自動でマッピングしてくれます。
以下のコード例の場合は, `{"name":"taro", "age": 20}`といったリクエストを受け付けます。

```kotlin
    @PostMapping("/user")
    fun postByJsonBody(
        @RequestBody request: UserRequestBody
    ): String {
        return "name: ${request.name}, age: ${request.age}"
    }
    
    data class UserRequestBody(
        val name: String,
        val age: Int
    )
```

実際に試してみると、以下のようになります。
``` shell
$ curl http://localhost:8082/user -X POST -d '{"name":"taro", "age":"25"}' -H "Content-type:application/json"
name: taro, age: 25
```

## 独自型への変換
受け取ったJsonをなんらかの型（値オブジェクトなど）に変換する場合、以下の2つの方法が考えられます。
1. コントローラ層（サービス層）で変換ロジックを直接書く
1. `@JsonConverter`コンポーネントを使用してリクエストを受け取った時点で変換させる

1.の方法は簡単ですね。例えば以下のように直接コンストラクタを呼び出してしまいます。
```kotlin
    @PostMapping("/user")
    fun postByJsonBody(
        @RequestBody request: UserRequestBody
    ): String {
        // UserName()でオブジェクトを生成するコードを書く
        val userName = UserName(request.name)
        return "name: ${request.name}, age: ${request.age}"
    }
```
ただし、上記の方法を用いる場合、`UserName`として扱いたいJsonをリクエストされた場合に、毎度変換の必要が出てきてしまいます。
そんな時に便利なのが2.の`@JsonComponent`です。
先にコードを見てしまいましょう。
```kotlin
@JsonComponent
class UserNameJsonConverter {
    class Serializer : JsonSerializer<UserName>() {
        override fun serialize(value: UserName, gen: JsonGenerator, serializers: SerializerProvider) {
            return gen.writeObject(value.value)
        }
    }
  
    class Deserializer : JsonDeserializer<UserName>() {
        override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): UserName {
            return UserName(p.valueAsString)
        }
    }
}
```
上記のように、`JsonSerializer<T>`と`JsonDeserializer<T>`の実装クラスをそれぞれ作成することで、Jsonのシリアライズ・デシリアライズを実行してくれます。
あとは前述した`UserRequestBody`の`name`の型を`UserName`に変更すると、コントローラ側から変換ロジックを消すことができます。
```diff_kotlin
    data class UserRequestBody(
-        val name: String,
+        val name: UserName,
        val age: Int
    )
```

```diff_kotlin
    @PostMapping("/user")
    fun postByJsonBody(
        @RequestBody request: UserRequestBody
    ): String {
-        val userName = UserName(request.name)
        return "name: ${request.name}, age: ${request.age}"
    }
```

# URIパラメータ
`@PathVariable`を使用します。
## 基本
```kotlin
    @GetMapping("get/{userId}")
    fun getUser(@PathVariable userId : String): String {
        return "useId: $userId"
    }
```
これも実際に`curl`で実行してみます。
```shell
$ curl http://localhost:8082/user/20 -X GET
useId: 20
```
## 独自型への変換
`@PathVariable`についても、Jsonと同様に型の変換を実行する方法があります。
`Converter<S, T>`を実装したコンポーネントを作成することで、`String`で受け取ったパラメータを独自の型に変換してくれます。
```kotlin
@Component
class UserIdConverter : Converter<String, UserId> {
    override fun convert(source: String): UserId {
        return UserId(source)
    }
}
```

```kotlin
data class UserId(
    val value: UUID
) 
```
あとは同様に、コントローラの受け取る`@PathVariable`を`UserId`型に変換すればOKです。
```diff_kotlin
    @GetMapping("user/{userId}")
    fun getUserByPathVariable(
-        @PathVariable userId : String
+        @PathVariable userId : UserId
    ): String {
        return "useId: $userId"
    }
```
# クエリストリング
クエリストリングは`@RequestParam`を使用します。
## 基本
```kotlin
    @GetMapping("get/user")
    fun getUserByQueryParams(
        @RequestParam id: String
    ): String {
        return "id: $id"
    }
```
こちらも同様に`curl`で試してみます。
```shell
$ curl 'http://localhost:8082/get/user?id=22'
id: 22
```

## 独自型への変換
`@PathVariable`と同様に、`Converter<S, T>`の実装クラスのコンポーネントを作成します。

# 終わりに
上記の説明はごく基本的な使用方法に限りますので、より詳細な方法は公式ドキュメントなど、参考にしてください。
それでは良い`Spring`ライフを!

# 参考
https://www.baeldung.com/spring-pathvariable

https://www.baeldung.com/spring-request-response-body

https://www.baeldung.com/spring-request-param