リクエストを任意のクラスにバインドして受け取る

:::note warn
最新版は[こちら](https://hirabay.net/?p=442)
:::

https://hirabay.net/?p=442

# 概要

「HttpServeletRequest」からヘッダーやパラメータ等を取得したい時、
とりあえず

```java
public Oject sample(HttpServletRequest request) {
    // 処理
}
```
としておけば、コントローラークラスでどうとでもできるが、
コントローラークラスの責務が多くなるので、
必要な値だけ、適切なオブジェクトにバインドして受け取りたいと思った。

# 方法
`HandlerMethodArgumentResolver`を使えばいけそう！

# 詳細
`HandlerMethodArgumentResolver`の使い方だけ理解したいので
簡単にリクエストパラメータの値をStringではなく自作のクラスで受け取れるようにしてみる

## パラメターを受け取るクラス

```java
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

@Getter
@AllArgsConstructor
@ToString
public class SampleArgument {
    private String value;

    public static SampleArgument of(String value) {
        return new SampleArgument(value);
    }
}
```
個人的な好みで、インスタンス化は`of`メソッドでできるようにしています。
引数で受けとった文字列を`value`に保持するシンプルなクラス。

## HandlerMethodArgumentResolver
本題の任意のクラスにバインドするためのクラス

```java
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

@Component
public class SampleResolver implements HandlerMethodArgumentResolver {

    // 詳細は割愛。。trueの場合のみresolveArgumentが実行されます
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return true;
    }

    // ここで返却するオブジェクトが、引数として利用可能になります
    @Override
    public Object resolveArgument(
            MethodParameter       parameter,
            ModelAndViewContainer mavContainer,
            NativeWebRequest      webRequest,
            WebDataBinderFactory  binderFactory
    ) throws Exception {

        String value = webRequest.getParameter("value");
        return SampleArgument.of(value);
    }
}
```
`HandlerMethodArgumentResolver`インタフェースを実装します。
ここではBeanとして登録するために`@Component`を付与しています。

## 実装したHandlerMethodArgumentResolverを有効にする

```java
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;

@Configuration
@AllArgsConstructor
public class HandlerMethodArgumentResolverConfiguration implements WebMvcConfigurer {
    private SampleResolver sampleResolver;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(sampleResolver);
    }
}
```
先ほど@ComponentをつけたのでResolverはコンストラクタで受け取る（`@AllArgsConstructor`で自走生成）
ポイントは
・`WebMvcConfigurer`インタフェースを実装すること
・`@Configuration`をクラスに付与すること
あとは`addArgumentResolvers`の実装メソッドでリストに実装したResolverを追加してあげればOK！

# 動作確認
以下のようなコントローラーを用意しました。

```java
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/HandlerMethodArgumentResolver")
@Slf4j
public class HandlerMethodArgumentResolverController {
    @GetMapping("/sample")
    @ResponseBody
    public SampleArgument sample(SampleArgument sampleArgument) {
        log.info("sampleArgument: {}", sampleArgument);
        return sampleArgument;
    }
}
```
http://localhost:8080/HandlerMethodArgumentResolver/sample?value=HelloWorld
にアクセスすると

```
sampleArgument: SampleArgument(value=HelloWorld)
```
自作クラスにバインドできたことが確認できた！！