LoginSignup
2
1

More than 5 years have passed since last update.

JAX-RS @BeanParam のパラメータクラスと JavaEE7 の BeanValidation の順番指定

Last updated at Posted at 2017-01-08

ことのあらまし

実装としてやりたいこと

  1. JAX-RS で @BeanParam でパラメータを受け取る
  2. @BeanParam のクラスはいくつかのフィールドを別のクラスに依存している
  3. JavaEE7 の BeanValidation で妥当性チェックを行う
  4. BeanValidation ではグループを使用して検査順序を指定する
  5. 特定のグループでのエラーか確認したい

シチュエーション

  1. HTTPのヘッダー情報から得るデータは1つのクラスにまとめておきたい
  2. ヘッダー情報のエラーと、クライアント UI から渡される情報のエラーは応答を区別したい

事情としては、ヘッダー情報はクライアント UI で入力しないので、入力エラーと同じように応答を返しても、クライアント側は何も対処のしようがないので、システム的におかしいよ、ということを伝えたい。

環境

JavaEE7 ということで以下の前提。

  1. JAX-RS 2.0
  2. BeanValidation 1.1.0

実際の稼動確認は WebSphere Liberty Profile 8.5.x.6 で行った。

実装の形

グループ指定用インターフェース

HeaderValidationGroup.java
public interface HeaderValidationGroup {
  // 中身不要
}

複数のエンドポイントでパラメータの妥当性検査の順序指定に使用する想定のインターフェース。

パラメータクラス

共通ヘッダ

HeaderParam.java
// このクラスの検査順序指定 ( HeaderParam.class の指定も必要 )
@GroupSequence({ HeaderValidationGroup.class, HeaderParam.class })
public class HeaderParam {
    @HeaderParam("header.h1") // Param のフィールド名を指定する => "h1" だけではダメ X-<
    @NotNull(groups = HeaderValidationGroup.class)
    private String h1;

    @HeaderParam("header.h2")
    @NotNull(groups = HeaderValidationGroup.class)
    private String h2;
}

エンドポイント固有のパラメータ

Param.java
public class Param {
    @BeanParam
    @Valid
    private HeaderParam header;

    @QueryParam("p1")
    @Pattern(
    private String p1;

    @QueryParam("p2")
    private String p2;
}

エンドポイント

EndPoint.java
@Path("/endpoint")
public class EndPoint {
    @Inject
    private Validator validator;

    @GET
    @Path("service1")
    @Produces(MediaType.APPLICATION_JSON)
    public Response service1(@BeanParam Param param) {
        Set<ConstraintValidation<T>> results = validator.validate(param);
        if (results.size() == 0) {
            // 実処理
            return Response ・・・
        } 
        // 検出したエラーの ConstraintValidation が保持する情報から groups を手繰っていく
        boolean hasHeaderError = 
            results.stream().anyMatch(c->c.getConstraintDescriptor().getGroups().stream()
                .anyMatch(HeaderValidationGroup.class::isAssignableFrom));
        return Response.status(
            hasHeaderError ? Status.INTERNAL_SERVER_ERROR : Status.BAD_REQUEST).build();
    }
}

一応上記で「ことのあらまし」に書いたことは実現できた。
しかし、@HeaderParam("header.h1") という書き方はなんとかしたい・・・。
Param クラスのフィールド名を prefix としてつけるのはできれば避けたい。

   curl -X GET --header 'header.h1: value_for_h1' ・・・

ではなくて、

   curl -X GET --header 'h1: value_for_h1' ・・・

と、したい。


Happy Programming!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1