Webx框架:表单验证

时间:2022-10-27 13:37:42
表单验证

传统的应用中,表单中的数据是放在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