JavaのSpring Bootにおいて、クエリパラメータのキー名と引数名が異なる場合のフォームクラスの書き方

こんにちは。 エキサイト株式会社の三浦です。

今回は、Java / Spring Bootを使っているとき、GETリクエスト受付時のクエリパラメータのキーと受け取り変数名が異なる場合の、受け取り用クラス(フォームクラス)の書き方について説明します。

課題

突然ですが、あなたは今Java / Spring Bootを使ってAPIを開発しているとします。 RESTのGETリクエストにおいてクエリパラメータを受け付ける処理を書く時、クエリパラメータのキー名がそのまま受け取る変数名として使えるのであればいいのですが、どうしてもキー名と変数名が異なることがあります。 たとえば、クエリパラメータのキーはスネークケースだが変数名はキャメルケースにしたい、という経験がある方は多いのではないでしょうか。

@RequestParam を使って

@GetMapping()
public String getSampleData(
    @RequestParam(value = "data_a") String dataA,
    @RequestParam(value = "data_b") String dataB,
    @RequestParam(value = "data_c") String dataC,
    @RequestParam(value = "data_d") String dataD
) {
    return "Hello world!";
}

と書けるのであればいいですが、キーが大量にあって1つ1つ引数を書いていると可読性に大きな問題がある場合や、受取時に2つ以上の引数を同時に対象とするバリデーションを行いたい場合(たとえば、 data_adata_b のどちらかは必ず値があることをバリデーションしたい場合)は、引数にクラスを指定することになります。

@GetMapping()
public String getSampleData(
    @ModelAttribute SampleModel sampleModel
) {
    return "Hello world!";
}

@Data
public class SampleModel {
    private String data_a;
    private String data_b;
    private String data_c;
    private String data_d;

}

ただし、このままだと変数名がキー名と同じになってしまいます。

解決策1(コンストラクタの引数名をキー名にし、プロパティ名を使用したい変数名にする)

そこで、以下のようにすることでキー名を想定通りにすることが出来ます。

@Getter
public class SampleModel {
    private String dataA;
    private String dataB;
    private String dataC;
    private String dataD;

    public SampleModel(
        String data_a,
        String data_b,
        String data_c,
        String data_d
    ) {
        this.dataA = data_a;
        this.dataB = data_b;
        this.dataC = data_c;
        this.dataD = data_d;
    }
}

クエリパラメータからデータを受け取るのはフォームクラスのコンストラクタであるため、コンストラクタの引数名をキー名と同じにした上で、フォームクラスのプロパティ名を使用したい変数名とします。

ただしこの場合、コンストラクタの引数名はキー名と同じになってしまうことになります。

解決策2(ConstructorPropertiesを使用する)

コンストラクタの引数名の時点で使用したい変数名にしたい場合は、 ConstructorProperties を使います。

@Getter
public class SampleModel {
    private String dataA;
    private String dataB;
    private String dataC;
    private String dataD;

    @ConstructorProperties({"data_a", "data_b", "data_c", "data_d"})
    public SampleModel(
        String dataA,
        String dataB,
        String dataC,
        String dataD
    ) {
        this.dataA = dataA;
        this.dataB = dataB;
        this.dataC = dataC;
        this.dataD = dataD;
    }
}

ConstructorProperties がクエリパラメータのキーを紐付けてくれます。

まとめ

フォームクラスを使ってクエリパラメータのキーを異なる名前の変数に紐付けたい場合は、コンストラクタや ConstructorProperties を使うと良いでしょう。 プロジェクトのルールにもよりますが、引数名から使用する変数名と同じにしたいというケースが多いと思いますので、 ConstructorProperties を使用する場面が多いのではないでしょうか。