Webx框架:表单验证

时间:2022-10-27 13:38:06
表单验证

传统的应用中,表单中的数据是放在Java代码中进行检验的。这种方法在编写java代码的时候很无聊,而且后期的修改也不方便。 而webx框架将表单验证的逻辑独立放在配置文件中。这种方式更加方便。

webx中,表单的验证有三个步骤:

  1. 定义验证规则
  2. 在Java代码中调用该规则
  3. 在页面中显示验证结果。

定义验证规则。下面是验证规则的一个例子。

  
  
 
 
<services:form xmlns="http://www.alibaba.com/schema/services/form/validators">
  <services:group name="register">
    <services:field name="userId" displayName= "登录名" >
      <required-validator>
        <message> 必须填写 ${displayName}</message>
      </required-validator>
      <regexp-validator pattern="^[A-Za-z_][A-Za-z_0-9]*$">
        <message>${displayName} 必须由字母、数字、下划线构成 </message>
      </regexp-validator>
      <string-length-validator minLength="4" maxLength="10">
        <message>${displayName} 最少必须由 ${minLength} 个字组成,最多不能超过 ${maxLength} 个字 </message>
      </string-length-validator>
    </services:field>
  </services:group>
</services:form>

上面这个例子中,最外层是form,表示表单验证服务。一个验证服务中可以包含多个group,表示验证组。 一个验证组中可以包含多个field,表示验证字段。每个验证字段中可以包含多个validator,表示验证规则。 每个验证规则只能包含一个错误消息。

编写Java代码。下面是Java代码的一个例子。

  
  
 
 
public class AddUserAction {
  public void doAdd(@FormGroup("register") MyUser userNavigator nav) throws Exception {
    save(user);
    nav.redirectTo("registerSuccess");
  }
}

上面这个例子中使用了@FormGroup注解,它的意思是通过register规则校验表单, 如果校验失败会让用户重新填写表单,如果校验成功,就会执行方法中的代码。 AddUserActiondoAdd这些符号需要告诉框架,在后面会详细讲解。

编写表单。首先要定义表单工具,这样才能在页面中通过$form进行访问。

  
  
 
 
<services:pull xmlns="http://www.alibaba.com/schema/services/pull/factories">
  <form-tool />
  ...
</services:pull>

表单内容。

  
  
 
 
<form method="post" action="">
 
  <!-- 获取一个名为register的Group验证规则。由于每个group可以有多个实例,这里只使用默认的实例。-->
  #set ($group = $form.register.defaultInstance)
 
  <!-- 用户名字段。 -->
  <input type="hidden" name="username" value="$!group.username.value"/>
  <!-- 用户名验证错误消息 -->
  #if(!$group.username.valid)
    $group.username.message
  #end
 
  <!--其他字段也是类似,这里就不赘述了。-->
 
  <!--提交表单时交给AddUserAction进行处理。-->
  <input type="hidden" name="action" value="AddUserAction"/>
 
  <!--提交表单时调用doAdd方法-->
  <input type="submit" name="event_submit_do_add" value="提交"/>
 
</form>
 
<!-- 表单默认值。配置方法如下。 -->
<services:field name="myfield" defaultValue="mydefault" ...>

修改老数据。有时候项目的需求比如是修改账户资料。那么在打开修改页面的时候,表单中就已经有数据了, 那么这种需求该怎么做呢?只要在Java代码中将数据填入到Context中,再将数据填充给group即可。

Java代码:将数据放在context中。

  
  
 
 
public void execute(Context context) throws Exception {
  User user = ...;
  context.put("user", user);
}

页面中:将user变量中的数据填充到group中:

  
  
 
 
#set ($group = $form.userAccount.defaultInstance)
$group.mapTo($user)

如果$user没有定义,那么mapTo会忽略。

postonly属性。这个属性的作用是让表单只支持post方式提交,这样会略微提高CSRF的攻击难度。它的配置方法如下:

  
  
 
 
<services:form postOnlyByDefault="true">
  <services:group name="group1" postOnly="true" />
</services:form>

trimming参数。目的是去除字段值两端的空格。下面是配置的例子:

  
  
 
 
<services:form>
  <services:group name="group1" trimmingByDefault="true">
    <services:field name="field1" trimming="true" />
  </services:group>
</services:form>

displayName参数。目的是让错误消息中重复使用字段的显示名称。下面是配置方法:

  
  
 
 
<services:field name="field1" displayName= "我的字段" >
  <required-validator>
    <message> 必须填写 ${displayName}</message>
  </required-validator>
</services:field>

类型转换。表单中的值只能用字符串表示,如果想要转换成Java中的类型,就需要声明类型转换器。

  
  
 
 
<services:form converterQuiet="true">
  <services:property-editor-registrar
      class="com.alibaba.citrus.service.configuration.support.CustomDateRegistrar"
      p:format="yyyy-MM-dd" />
</services:form>

验证消息国际化。目的是让验证消息支持多语言。下面这段配置告诉框架,消息文本需要从form_msgs***中加载。

  
  
 
 
<bean id="messageSource"
      xmlns="http://www.springframework.org/schema/beans"
      class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
      p:defaultEncoding="GB18030">
 
  <property name="basenames">
    <list>
      <value>form_msgs</value>
    </list>
  </property>
</bean>

下面这段验证规则,消息内容会对应到ResourceBundle中的form.register.userId.required中:

  
  
 
 
<services:form>
  <services:group name="register">
    <services:field name="userId">
      <required-validator id="required" />
    </services:field>
  </services:group>
</services:form>

表单拆分。有时候表单的数量非常多,放在一个文件中不方便维护,这时就需要将form标签进行拆分,分配到多个文件中。 配置方法如下。例子中的primary属性表示框架会从这个form开始加载验证规则,而不是从其他文件中进行加载。

  
  
 
 
<beans:import resource="inc/form_part1.xml" />
<beans:import resource="inc/form_part2.xml" />
<services:form xmlns="http://www.alibaba.com/schema/services/form/validators" primary="true">
  <services:import form="part1" />
  <services:import form="part2" />
  ...
</services:form>

其他文件中的form配置如下:

  
  
 
 
<services:form id="part1">
  ...
</services:form>
 
<services:form id="part2">
  ...
</services:form>

Group继承。有时候两个Group之间有很多相似的地方。这时就可以用继承来消除重复。

  
  
 
 
<services:group name="baseGroup">
  ...
</services:group>
<services:group name="subGroup" extends="baseGroup">
  ...
</services:group>

默认值:

  
  
 
 
<services:field name="field1" defaultValue="defaultValue" />

验证错误消息。它可以包含变量,下面是一个例子。

  
  
 
 
<string-length-validator minLength="4" maxLength="10">
  <message>${displayName} 最少必须由 ${minLength} 个字组成,最多不能超过 ${maxLength} 个字 </message>
</string-length-validator>

验证器。有以下几种:

名称 作用
required-validator 必选验证器。
regex-validator 正则验证器
string-length-validator 字串长度验证器
string-byte-length-validator 字节长度验证器
string-compare-validator 字符串比较验证器。属性有equalTonotEqualToignoreCase
mail-address-validator 邮箱验证器
number-validator 数字验证器
number-compare-validator 数字比较验证器
date-validator 日期验证器
uploaded-file-validator 文件上传验证器
csrf-validator 验证csrf字段
custom-error 自定义验证
multi-values-count-validator 多值验证

number-validator数字验证器。属性有以下几个:

  • numberType 数字的类型。可用的类型为:intlongfloatdoublebigDecimal。 如不设置,默认值为int
  • equalTo 可选数字范围:要求数字等于指定值。
  • notEqualTo 可选数字范围:要求数字不等于指定值。
  • lessThan 可选数字范围:要求数字小于指定值。
  • lessThanOrEqualTo 可选数字范围:要求数字小于或等于指定值。
  • greaterThan 可选数字范围:要求数字大于指定值。
  • greaterThanOrEqualTo

number-compare-validator 数字比较验证器。有以下几个属性:

  • numberType 数字的类型。可用的类型为:int、long、float、double、bigDecimal。如不设置, 默认值为int。
  • equalTo
  • notEqualTo 可选数字范围:要求数字不等于指定字段的值。
  • lessThan 可选数字范围:要求数字小于指定字段的值。
  • lessThanOrEqualTo 可选数字范围:要求数字小于或等于指定字段的值。
  • greaterThan 可选数字范围:要求数字大于指定字段的值。
  • greaterThanOrEqualTo

date-validator 日期验证器。可以指定的属性有:

  • format 日期的格式,如不指定,默认为yyyy-MM-dd。
  • minDate 可选的日期范围:最早的日期。该日期格式也是用format参数来表示的。
  • maxDate

uploaded-file-validator 文件上传验证器。可以验证以下属性。

  • minSize 最小文件尺寸。可使用K/M等单位,例如:10K、1M等。
  • maxSize 最大文件尺寸。可使用K/M等单位,例如:10K、1M等。
  • extension 允许的文件名后缀,多个后缀以逗号分隔。例如:gif、jpg、png。注意,文件名是由浏览器传递给服务器的,因此验证器并不能保证文件的扩展名和内容一致。例如,xxx.jpg有可能是一个exe可执行文件。
  • contentType

custom-error 自定义验证。将验证逻辑放在action中,其余特性与普通的验证器一样。首先要增加CustomErrors参数,错误通过setError进行设置。

  
  
 
 
public void doRegister(@FormField(name = "userId", group = "register") CustomErrors err, ...) throws Exception {
  try {
    ...
  } catch (DuplicatedUserException e) {
    Map<StringObject> params = createHashMap();
    params.put("userId", user.getUserId());
    err.setMessage("duplicatedUserId", params);
  }
}

验证器还可以增加条件验证,有if、choose、all-of、none-of、any-of。

  
  
 
 
<if test="commentCode.value == 'other'">
  <required-validator>
    <message> 必须填写 ${displayName}</message>
  </required-validator>
</if>
<all-of>
  <validator />
  <validator />
</all-of>

多值验证。可以验证值的数量范围。

  
  
 
 
<multi-values-count-validator minCount="1" maxCount="3">
  <message> 至少选择 ${minCount} 项,最多选择 ${maxCount} 项 </message>
</multi-values-count-validator>

还可以对各个值进行验证。

  
  
 
 
<all-of-values>
  <message>${allMessages}</message>
  <validator />
  <validator />
</all-of-values>
<any-of-values>
<mess    age> 至少有一个 ${displayName} 要符合要求 </message>
<validator />
<validator />
</any-of-values>
<none-of-values>
  <message> 所有 ${displayName} 都不能符合要求 </message>
  <validator />
  <validator />
</none-of-values>

form-tool对象。首先要声明pull服务。

  
  
 
 
<services:pull xmlns="http://www.alibaba.com/schema/services/pull/factories">
<form-tool />
...
</services:pull>

这样在页面中就可以直接引用$form对象了。 它的接口如下:

  
  
 
 
$form.valid
#set ($group = $form.group1.defaultInstance) 取得group1的默认实例,如果不存在,则创建之。
#set ($group = $form.group1.getInstance("id")) 取得group1的指定id的实例,如果不存在,则创建之。
#set ($group = $form.group1.getInstance("id", false)) 取得group1的指定id的实例,如果不存在,则返回null。
#foreach ($group in $form.groups) ... #end 遍历当前form中所有group实例。
#foreach ($group in $form.getGroups("group1")) ... #end

$group对象有以下接口:

  
  
 
 
#if ($group.valid) ... #end 判断当前group是否验证为合法,或者未经过
验证(即初始表单)
#if ($group.validated) ... #end 判断当前group是否经过验证(初始表单为未
经过验证的表单)
$group.field1 取得field1
#foreach ($field in $group.fields) ... #end 遍历当前group中所有的fields
$group.mapTo($bean)

$field对象提供的接口:字段的名称、字段值、多值、是否验证通过、错误消息。

  
  
 
 
<input type="text" name="$field.key" value="$!field.value" />
#if (!$field.valid)
  <div class="error">$field.message</div>
#end
#foreach ($value in $field.values) ... #end
$field.getAbsentHiddenField($value)

在字段中附上一个Java对象。

  
  
 
 
$field.setAttachment($obj)
$field.attachmentHiddenField