HandlerMethodArgumentResolverで任意のクラスをControllerで受け取る

ControllerでHttpServletRequestを引数に設定するとなんでもできますが、同じようなことをいろんなContorllerでやっていたらコードが重複して無駄ですよね…

例えばこんな感じ(このレベルだと@RequestHeader使おうで済むんですけどねw)

    @GetMapping("/sample/plain")
    public String plain(HttpServletRequest request) {
        return request.getHeader("key");
    }

この記事では、HandlerMethodArgumentResolverを使用して、Controllerの引数で任意のクラスを受け取ることで解決する方法を紹介します!

任意クラスの作成

まずは、Controllerの引数で受けとるクラスを作成します。

@Value
public class SampleModel {
    private String key;
}

【本題】HandlerMethodArgumentResolver

本題の任意のクラスに値をバインドするクラスです。

public class SampleArgumentResolver implements HandlerMethodArgumentResolver {
    // Controller(RestController)の引数ごとに実行される
    // このメソッドの戻り値がtrueの場合だけ、resolveArgumentが実行される
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(SampleModel.class);
    }

    // 引数を解決するメインメソッド
    @Override
    public Object resolveArgument(
            MethodParameter parameter,
            ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory
    ) throws Exception {
        var name = webRequest.getHeader("key");
        // 引数で受け取りたいクラスを返す
        return new SampleModel(name);
    }
}

メソッドごとにもう少し解説します。

supportsParameter

このメソッドは、Controllerの実行ごとControllerの引数ごとに実行されます。

このメソッドがtrueを返す場合にのみresolveArgumentが実行されます。

一番メジャーな使い方としては、引数のクラスに応じてtrue / falseを返却することです。

もう少し細かくやろうとしたら
・引数の名前:parameter.getParameter().getName()
・アノテーション:parameter.getParameterAnnotation(<クラス>.class)
とかが使えそうです!

resolveArgument

このクラスで引数に受け取りたいクラスを返すように実装します。

HttpServletRequestに対応するクラスがNativeWebRequestで、headerやパラメータ等HTTPリクエストに関する情報を取得できます。

WebMvcConfigurer

先ほど作成したHandlerMethodArgumentResolverですが、クラスを作成しただけではSpringに認識をしてもらえないので、WebMvcConfigurerを実装したクラスで存在を教えてあげる必要があります。

@Configuration
public class HandlerMethodArgumentResolverConfiguration implements WebMvcConfigurer {
    // 実装したresolverを登録する
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new SampleArgumentResolver());
    }
}

動かしてみる

これで、任意のクラス(ここではSampleModel)を受け取る準備ができたので、動かしてみましょう!

@RestController
public class SampleController {
    @GetMapping("/sample")
    public String resolver(
            SampleModel sampleModel
    ) {
        return sampleModel.getKey();
    }
}
$ curl -H 'key: value' 'localhost:8080/sample/resolver'
value

🎉🎉🎉🎉

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です