发博文
正文 字体大小:

Ajax validation with Struts 2 (1)

(2008-12-21 21:40:07)
标签:

struts2

ajax

validation

杂谈

分类: 转载

Support for Ajax and JavaScript takes the pain out of Web-form validation

Writing code to validate Web-form input can be even more of a chore than implementing form-processing logic. But help is at hand, thanks to the Struts 2 framework. Oleg Mikheev looks under the hood of the Struts 2 validation mechanism and shows you how its Java, JavaScript, and Ajax support can take the pain out of Web-form validation.

Web applications often require user input, which can range from simple username/password values to data entered into a complex form with dependent fields. The task of validating Web-form input is often more complex than implementing the logic to execute on the data after it's submitted. A validation framework can help simplify validation coding -- and the more complex your validation rules, the more pain the framework can spare you. Apache Struts was one of the first Web application frameworks designed to automate Web forms processing, and it is the best-known. This article explores the powerful form-validation options offered by Struts' successor, Struts 2. I'll delve briefly into Struts 2's underlying validation architecture, then show you how the framework supports both server- and client-side validation, including advanced Ajax validation. The article features an example Web application with full source code and binaries ready for deployment into a servlet container.

Struts 2's validation architecture

Struts 2 is based on WebWork, another powerful but less popular Web application framework. Struts 2 shares WebWork's approach to validation. The only difference is that WebWork relies on the validation framework in XWork 1, while Struts 2 uses the improved and customized XWork 2. XWork is a comprehensive command-pattern framework that does a huge part of the work related to Struts 2 configuration, instantiation, and runtime processing. (Although XWork is mostly known for its connection with WebWork and Struts 2, it can be used as a separate framework.)

The key component in XWork is Action. Action is basically a class that contains the code that you want to execute on a specific request originating from the browser. Another important XWork component is Interceptor. As its name suggests, Interceptor intercepts calls to Action to do more processing on them -- a mechanism quite similar to the concept of aspect-oriented programming (AOP). XWork comes with a number of already implemented interceptors, and Struts 2 adds some more of its own. You are free to implement your own custom interceptors too.

Struts 2 actions and interceptors are configured in a struts.xml file. Struts 2 looks for struts.xml in WEB-INF/classes (unlike Struts 1, which stores its struts.xml in the WEB-INF folder). Configurations can be grouped in a package -- another XWork component that represents a logical configuration unit that groups other components. Packages can be extended and overridden by "sub" packages. In most cases you won't create your Struts 2 configuration from scratch; instead it will extend the default configuration that is stored in the struts-default package. The Struts 2 default configuration is stored in the struts-core.jar archive's root folder in the struts-default.xml file. You can examine this file if you are interested in deeper exploration of Struts 2 defaults and internals.

Struts 2 interceptors can be grouped into an InterceptorStack. The struts-default package defines a number of interceptor stacks, but in most cases you'll use the defaultStack. Struts 2 validation is handled by a validation interceptor that is included in the defaultStack.

 

Taking action

Let's consider a simple Struts 2 action, called FirstAction, with three fields:

  • name:String
  • age:Integer
  • gender:Gender<Enum>

(I'll explain later why objects are preferred to simple Java types.) Depending on the value of gender:Gender<Enum>, FirstAction opens either Male.jsp or Female.jsp, as shown in Listing 1:

Listing 1. Struts configuration

 <struts>
   <package name="test" extends="struts-default">
      <action name="First" class="struts2validation.FirstAction">
         <interceptor-ref name="defaultStack"/>
         <result name="female">/Female.jsp</result>
         <result>/Male.jsp</result>
      </action>
   </package>
</struts>


The name attribute can be omitted from one of the two <result> tags. That result's name automatically defaults to success.

Server-side validation

Once an HTML form is submitted, the request is intercepted by all interceptors from the defaultStack, including validation, which is an XWork ValidationInterceptor. Starting with Struts 2.0.4, the XWork ValidationInterceptor is extended by AnnotationValidationInterceptor, which adds functionality to disable validation of action methods that are annotated by a @SkipValidation annotation.

The validation framework looks for an XML validation configuration whose name is ActionClass-validation.xml, where ActionClass is the name of the related action class. The validation configuration must be located in the same package as the action class itself. Because a single action class could be used in different actions, it is possible to have a validation configuration for each of the actions, in which case the file should be named ActionClass-ActionName-validation.xml. Struts 2 supports object-oriented programming concepts and considers validation configurations for all classes that the action extends and all interfaces that it implements.

Now I'll demonstrate a simple validation. I'll create a validation configuration for the FirstAction class and call it FirstAction-validation.xml.

First, though, I need to create a FirstInput.jsp file with a form, using Struts 2 custom tags. Custom tags use templates for rendering HTML code. Themes are collections of templates grouped by their output methods. I'll discuss themes later. Right now, you only need to know that four themes come with Struts 2:

  • simple (so simple that it doesn't provide validation support)
  • xhtml
  • css_html
  • ajax

The example form will use the xhtml theme. The <s:actionerror> tag displays any action errors. The <form> tag produces an HTML form with two HTML inputs (a <s:textfield> tag and a <s:submit> tag for a Submit button). Listing 2 shows the form for FirstInput.jsp:

Listing 2. FirstInput.jsp form

 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
  <body><s:actionerror/>
  <s:form action="First" theme="xhtml">
      <s:textfield label="Name" name="name" required="true"/>
      <s:textfield label="Age" name="age" required="true"/>
      <s:textfield label="Gender (Male/Female)" name="gender"/>
      <s:submit/>
  </s:form>
  </body>
</html>

Note that the required attribute in the <s:textfield> element has nothing to do with validation. It is used by some templates to display an asterisk next to the field.

Gender is a Java Enum, which needs to be handled specifically. Type conversion in Struts 2 is beyond this article's scope, so I'll just create a FirstAction-conversion.properties file for FirstAction in the same folder, with this content:

 gender=com.opensymphony.xwork2.util.EnumTypeConverter

FirstAction, shown in Listing 3, uses simple logic to distinguish between male and female:

Listing 3. FirstAction class

 public class FirstAction extends ActionSupport {
  public String execute() throws Exception {
        if(Gender.Female.equals(gender)) {
            return "female";
        } else {
            return SUCCESS;
        }
    } ...

If the user enters Male in the form, the success result (which is mapped to Male.jsp) returns. Even without seeing any validation work yet, you can probably already get the feel of Struts 2 validation. If you enter a nonnumeric value in the age field and submit the form, a validation message will appear: "Invalid field value." The same is true for the gender field: you can only enter Male or Female there; otherwise a validation message appears. This is the result of the conversionError interceptor from the defaultStack making sure that all parameters are of the type that the action can accept.

 

Now it's time to introduce some custom validation into the form. The simplest XWork validator that comes with Struts 2 is the required validator. It only checks for null values and is often confused with requiredstring, which checks for empty strings and can be instructed to trim strings before the check. Since in the case of the example all three parameters are always present in the request, there is no need for the required validator, and I'll use requiredstring. I'll put it into the FirstAction-validation.xml file for the FirstAction, as shown in Listing 4:

Listing 4. Action validation configuration: requiredstring validator

 <validators>
   <validator type="requiredstring">
      <param name="fieldName">name</param>
      <message>Name is required</message>
   </validator>...

This results in a "Name is required" message appearing after form submission if nothing was entered in the name input field.

You can configure a field to have several validations applied. To make the configuration file more readable and maintainable, you can configure validators in two ways:

  • Field-wise: You nest <field-validator> entries in the <field> entry.
  • Validator-wise: You create a standalone <validator> entry. The standalone validator can either work on its own or be tied to a certain field with a <param name=fieldName> entry.

A name-validation configuration doing exactly the same job as Listing 4, but using field-wise validation, could look like Listing 5:

Listing 5. Action validation configuration: Field-wise validation

 <field name="name">
   <field-validator type="requiredstring">
      <param name="trim">true</param>
      <message>Name is required</message>
   </field-validator>
</field>...

To take advantage of multiple validators per one field, Listing 6 introduces a requirement for age to be a required field whose value must be between 21 and 122 inclusive:

Listing 6. Action validation configuration: Multiple validations per field

 <field name="age">
   <field-validator type="required">
      <message>Age is required</message>
   </field-validator>
   <field-validator type="int">
      <param name="min">21</param>
      <message>You must be an adult</message>
   </field-validator>
   <field-validator type="int">
      <param name="max">122</param>
      <message>The oldest human ever was 122 years old</message>
   </field-validator>
</field>...

The required validator in Listing 6 tries to receive a value from the object graph. This value will already be of the exact type defined in the action. That's why it is impossible to check simple Java types with the required validator. Even if field values are empty (null), they are cast to a corresponding value, 0 in case of int.

阅读 评论 收藏 转载 打印举报
已投稿到:
  • 评论加载中,请稍候...

       

    验证码: 请点击后输入验证码 收听验证码

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 不良信息反馈 电话:4006900000 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有