groupsの指定

【前提条件】

[環境]
[参考資料]

Bean Validation1.1仕様書
The Java Community Process(SM) Program - communityprocess - final

[その他]

Validatorの生成はCDIを利用しています。

コンテキストパスは「Sample」、ApplicationPathは「/service」

【概要】

今回はValidatorのAPIにあるgroupsについてです。

groupsは入力チェックするプロパティをグループ化させ、
パターンごとにチェックする項目を帰ることができます。

【グループインターフェイス

groupsを使用する場合はグループごとに
インターフェイスを作成する必要があります。

インターフェイスは目印として使うだけなので、
メソッドの定義は不要です。

サンプルでは↓の2つを用意します。

public interface Group01 {}
public interface Group02 {}

【Bean】

import javax.validation.constraints.Size;
import javax.validation.groups.Default;
import javax.ws.rs.FormParam;
import jp.glory.sample03.groups.Group01;
import jp.glory.sample03.groups.Group02;

public class GroupBean {

    @Size(min = 10, groups = { Default.class })
    @FormParam("firstName")
    private String firstName = null;

    @Size(min = 10, groups = { Default.class })
    @FormParam("lastName")
    private String lastName = null;

    @Size(min = 10, groups = { Group01.class, Group02.class })
    @FormParam("telephone")
    private String telephone = null;

    @Size(min = 10, groups = { Group01.class })
    @FormParam("zipCode01")
    private String zipCode01 = null;

    @Size(min = 10, groups = { Group01.class })
    @FormParam("address01")
    private String address01 = null;

    @Size(min = 10, groups = { Group02.class })
    @FormParam("zipCode02")
    private String zipCode02 = null;

    @Size(min = 10, groups = { Group02.class })
    @FormParam("address02")
    private String address02 = null;

    @Size(min = 10)
    @FormParam("note")
    private String note = null;

    @FormParam("pattern")
    private String pattern = null;

    // アクセサメソッドは省略
}

groupsに先ほど作成したインターフェイスを配列で指定します。
複数のグループでチェックを行いたい場合は配列の中で複数指定します。

javax.validation.groups.Defaultアノテーション
ValidatorのAPIに標準で用意されているもので、
入力チェック時に必ずチェックが行われるようになります。

groupsに何も指定しない場合、空の配列を指定した場合は
Defaultと同じ挙動になります。

サンプルでは下のようにチェックを行っています。

Default Group01 Group02
firstName
lastName
telephone
zipCode01
address01
zipCode02
address02
note

【Validator】

グループを指定してチェックを行う場合は
Validator#validateにグループの配列を指定します。

Defaultのみを実行させたい場合は↓のようにします。

validator.validate(bean, new Class[] { Default.class });
validator.validate(bean, new Class[] {});
validator.validate(bean);

各グループのみを実行する場合は↓のようにします。
Defaultで指定したプロパティのチェックは行われません。

validator.validate(bean, new Class[] { Group01.class });
validator.validate(bean, new Class[] { Group02.class });

複数のグループを実行する場合は↓のようにします。

validator.validate(bean, new Class[] { Default.class, Group01.class });
validator.validate(bean, new Class[] { Default.class, Group02.class });
validator.validate(bean, new Class[] { Default.class, Group01.class, Group02.class });

【サンプル】

今回のサンプルはグループを利用して、
パターンごとにチェックする項目を変えます。

import java.text.ParseException;
import java.util.Set;
import javax.inject.Inject;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.groups.Default;
import javax.ws.rs.BeanParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import jp.glory.sample03.bean.GroupBean;
import jp.glory.sample03.groups.Group01;
import jp.glory.sample03.groups.Group02;

@Path("sample03")
public class GroupSampleResource {
    
    @Inject
    private Validator validator = null;
    
    @POST
    public Response postRequest(@BeanParam final GroupBean bean) throws ParseException {

        final Class<?>[] groups = getGroups(bean);
        final Set<ConstraintViolation<GroupBean>> result =  validator.validate(bean, groups);

        if (result.isEmpty()) {

            return createSuccessResponse();
        }

        return createErrorResponse(result);
    }

    private Class<Object>[] getGroups(final GroupBean bean) {

        if ("2".equals(bean.getPattern())) {
            return new Class[] { Default.class, Group01.class };
        }

        if ("3".equals(bean.getPattern())) {
            return new Class[] { Default.class, Group02.class };
        }

        if ("4".equals(bean.getPattern())) {
            return new Class[] { Default.class, Group01.class, Group02.class };
        }

        if ("5".equals(bean.getPattern())) {
            return new Class[] {};
        }

        return new Class[] { Default.class };
    }

    private Response createErrorResponse(final Set <ConstraintViolation<GroupBean>> validateResult) {
        
        final StringBuilder builder = new StringBuilder();
        
        builder.append("<html>");
        builder.append("<head>");
        builder.append("<meta charset=\"utf-8\">");
        builder.append("<title>Validation</title>");
        builder.append("</head>");
        builder.append("<body>");

        for (final ConstraintViolation<GroupBean> result : validateResult) {
            
            builder.append("<div>");
            builder.append(result.getPropertyPath().toString());
            builder.append(" : ");
            builder.append(result.getInvalidValue());
            builder.append(" : ");
            builder.append(result.getMessage());
            builder.append("</div>");
        }

        builder.append("</body>");
        builder.append("</html>");
     
        return Response.ok(builder.toString()).build();
    }

    private Response createSuccessResponse() {
        
        final StringBuilder builder = new StringBuilder();
        
        builder.append("<html>");
        builder.append("<head>");
        builder.append("<meta charset=\"utf-8\">");
        builder.append("<title>Validation</title>");
        builder.append("</head>");
        builder.append("<body>");
        builder.append("<div>Input value is valid!</div>");
        builder.append("</body>");
        builder.append("</html>");
     
        return Response.ok(builder.toString()).build();
    }
}
<!DOCTYPE html>
<html>
    <head>
        <title>Base Validation</title>
        <meta charset="UTF-8" />
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <style type="text/css">
            p.label {
                background: #00ffff;
                font-weight: bold;
            }
        </style>
    </head>
    <body>
        <form method="POST" id="validateForm" action="../service/sample03" >
            <p class="label">Default</p>
            <div>
                <span>First Name : </span>
                <input type="text" name="firstName" value="" />
            </div>
            <div>
                <span>Last Name : </span>
                <input type="text" name="lastName" value="" />
            </div>
            <p class="label">Group01 and Group02</p>
            <div>
                <span>Phone : </span>
                <input type="text" name="phone" value="" />
            </div>
            <p class="label">Group01</p>
            <div>
                <span>Zip Code : </span>
                <input type="text" name="zipCode01" value="" />
            </div>
            <div>
                <span>Address : </span>
                <input type="text" name="address01" value="" />
            </div>
            <p class="label">Group02</p>
            <div>
                <span>Zip Code : </span>
                <input type="text" name="zipCode02" value="" />
            </div>
            <div>
                <span>Address : </span>
                <input type="text" name="address02" value="" />
            </div>
            <p class="label">No Mark</p>
            <div>
                <span>Note : </span>
                <input type="text" name="note" value="" />
            </div>
            <p class="label">Pattern</p>
            <div>
                <select name="pattern">
                    <option value="1">Default</option>
                    <option value="2">Default + Group01</option>
                    <option value="3">Default + Group02</option>
                    <option value="4">Default + Group01 + Group02</option>
                    <option value="5">No Mark</option>
                </select>
            </div>
            <div>
                <input type="submit" value="送信" />
            </div>
        </form>
    </body>
</html>

【まとめ】

groupsを使用するとValidatorのAPI内で
チェックする項目を分けることができました。

実際に使う場合は今回のサンプルのように
パターンごとに判定を行い、必要なグループを選択する形になると思います。