struts2とseasar2でCRUD処理(validation編)

前回のCRUDアクション作成(struts2とseasar2でCRUD)ではCRUDの操作ができることを確認しましたが、入力値チェックはやっていませんでした。
今回はstruts2のvalidationの仕組みを使って入力値チェックを行います。

struts2では入力値チェックをValidation Interceptorで行います。
http://struts.apache.org/release/2.3.x/docs/validation-interceptor.html
http://struts.apache.org/release/2.3.x/docs/validation.html
このInterceptorでエラーとなった項目がある場合はWorkflow Interceptorで処理され"input"が返されます。
http://struts.apache.org/release/2.3.x/docs/workflow-interceptor.html
エラー時に返される値は、アクションにValidationWorkflowAwareを実装したり、メソッドに@InputConfigアノテーション参考)を付けることで上書きすることができます。
このためには、ValidationAwareをアクションに実装する必要があります。ActionSupportを使えばすでに実装されています。

バリデーションの指定方法は大きく3種類あります。

前の2つはデフォルトだと必ず処理されます。
両方とも外す場合は、struts.xml(struts-default.xml)でinterceptor-stackを定義する際にdeclarativeパラメータをfalseにする必要があります。

最後のvalidate()はアクションクラスがValidateableを実装している場合に処理されます。ActionSupportでは実装されています。
コレを無効にするには、interceptor-stackの定義でprogrammaticパラメータをfalseにする必要があります。

処理の順序としては(xml,アノテーション)->validate()メソッドの順です。

xmlの設定によるバリデーション

個人的に好みでないので詳細は省略します。
なぜかというと、設定ファイルが増えるのは「Convention Over Configuration」の考え方に反するから。
http://struts.apache.org/release/2.3.x/docs/validation.html

アノテーションによるバリデーション

まず、struts.xmlにあるinterceptor-stackの中身を次のように書き換える必要があります。
これをしないと、save()メソッドにアノテーションをつけている場合に、他の画面にリクエストがあった場合もこのアノテーションが処理されてしまいます。

<interceptor-ref name="validation">
    <param name="validateAnnotatedMethodOnly">true</param>
    <param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>

今回は、struts.xmlにまだinterceptor-stackが定義されていなかったので次のように定義しました。

  <package name="myapp" extends="convention-default">
    <interceptors>
      <interceptor-stack name="myapp-Stack">
   ~~ここはstrutx-default.xmlのdefaultStackの中身をコピーしてvalidationの部分を上の記述に書き換えます~~
      </interceptor-stack>
    </interceptors>
    <default-interceptor-ref name="myapp-Stack"/>
  </package>

アノテーションはフィールドに設定する方法とメソッドに設定する方法があります。
もちろん両方使ってもいいんですが、基本的には1つのアクションクラスに複数のメソッド(画面)がある場合はメソッドにのみ設定するのがいいのかなと思います。
詳細は本家をチェックしてください。
http://struts.apache.org/release/2.3.x/docs/annotations.html#Annotations-ValidationAnnotations

今回はcustomerのnameとemailフィールドを必須にします。
アクションクラスとjspファイルを書き換えます。

JspCrudAction
~~略~~
import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
import com.opensymphony.xwork2.validator.annotations.Validations;
~~略~~
	@Validations(
			requiredStrings = {
					@RequiredStringValidator(fieldName="customer.name", message="必須項目です"),
					@RequiredStringValidator(fieldName="customer.email", message="必須項目です")
			}
	)
	public String save() {
~~略~~
	}
~~略~~
jsp-crud-input.jsp
~~略~~
      <tr>
        <td>名前</td>
        <td><s:textfield name="customer.name" placeholder="名前"/><s:fielderror fieldName="customer.name"/></td>
      </tr>
      <tr>
        <td>E-Mail</td>
        <td><s:textfield name="customer.email" type="email" placeholder="E-Mail"/><s:fielderror fieldName="customer.email"/></td>
      </tr>
~~略~~

これで実行すると、名前をemailを入力しない場合にエラーメッセージが表示されるはずです。
表示デザインはイマイチですが、そこはCSSの設定をしてください。
もしくは、strutsタグのテンプレート自体を書き換えることもできます。詳細は省略します(参考)。

validateメソッドによるバリデーション

アクションクラスがValidateableをimplementsしている場合に処理されます。
あるメソッドxxxxが呼ばれる際に処理するメソッドは、validateXxxx()、validateDoXxxx()、validate()です。validateDoXxxx()はvalidateXxxx()がない場合にのみ処理されます。

今回はvalidate()とvalidateSave()を作ってみます。

JspCrudAction
	public void validate() {
		if (StringUtils.isNotBlank(customer.name)) {
			if (customer.name.length() < 2) {
				addFieldError("customer.name", "2文字以上が必須です");
			}
		}
		System.out.println("validate");
	}

	public void validateSave() {
		if (StringUtils.isNotBlank(customer.email)) {
			if (customer.email.length() < 4) {
				addFieldError("customer.email", "4文字以上が必須です");
			}
		}
	}

これらの処理はStringLengthFieldValidatorでも実現できますが、ここではあえてべた書きしてます。

コレを実行すると、
一覧表示の際と登録の際にvalidate()メソッドが呼ばれているのが分かると思います。
intercepter-stackの設定でinputをexcludeMethodsに指定しているのでinput()メソッドの場合はvalidate()メソッドは呼ばれません。

また、文字数チェックも正しく動いていることも確認できるはずです。

以上