ことのあらまし
実装としてやりたいこと
- JAX-RS で
@BeanParam
でパラメータを受け取る -
@BeanParam
のクラスはいくつかのフィールドを別のクラスに依存している - JavaEE7 の BeanValidation で妥当性チェックを行う
- BeanValidation ではグループを使用して検査順序を指定する
- 特定のグループでのエラーか確認したい
シチュエーション
- HTTPのヘッダー情報から得るデータは1つのクラスにまとめておきたい
- ヘッダー情報のエラーと、クライアント UI から渡される情報のエラーは応答を区別したい
事情としては、ヘッダー情報はクライアント UI で入力しないので、入力エラーと同じように応答を返しても、クライアント側は何も対処のしようがないので、システム的におかしいよ、ということを伝えたい。
環境
JavaEE7 ということで以下の前提。
- JAX-RS 2.0
- 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' ・・・
と、したい。