Webx 的表单验证服务学习经验
说说
我曾经使用过 Tornado 框架以及 Spring MVC 框架的表单系统, 他们的表单系统相当轻量级, 于是我以为 webx 框架的表单服务也差不多. 今天仔细看了一下官方文档, 发现 webx 确实有其独特的地方.
学习了表单验证, 如何写一个表单页面就没有问题了. 也并不需要先学会写一个表单然后再学表单验证服务, 直接开始学这个就OK.
验证形式
表单验证就是对用户输入的信息是否规范进行检查. 检查有三种形式:
- 数据传到服务器, 服务器在后台检查
- 数据在前端通过 js 来检查
- 通过 js 异步请求, 实现服务器异步验证.
目前 webx 框架实现了第一种形式. 后两种形式均需要在前端编写相应 js 代码方能完成. 相比之下 Tornado 框架并不自带服务端检查表单的功能, 完全需要靠 js + 后台api 来编写.
学习 webx 的表单验证服务就是学习这里的第一种形式.
webx 表单验证的特色
- **验证逻辑与表现逻辑分离
** 这就是说, 画表单的代码, 和对表单里面填的东西是否正确的验证代码, 是分开在不同的文件里面存放的. 这样可以多个表单共用一个验证器, 在修改验证器的时候也不会觉得两种代码杂糅在一起很烦. 我觉得这个特性非常棒!
- **验证逻辑和应用代码分离
** 这就是说, 验证代码也不用写在 java 文件里面. 优点同上.**
**
这两个分离, 就是说验证代码完全独立, 既不存在于前端渲染代码中, 也不存在于后端应用的业务逻辑代码中, 而是单独出来成为一个独立的部分. 这种程度的解耦真是美好:)
极简例子
下面假设表单的 url 是 http://localhost:8080/ljw/test_1.htm, 工程基于 com.alibaba.webx.tutorial1 进行开发.
**1. src/main/java/com….app1/module/screen/ljw/Test1.java
** 当用户发起第一次请求的时候, 此处的out会显示到网页上, 此外 vm 文件上的内容也会跟着显示出来.
public class Test1 {
@Autowired
private HttpServletResponse response;
public void execute() throws Exception {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("<h1>This is from Java</h1>");
}
}
2. src/main/java/com….app1/User.java
public class User {
private String id;
private String pwd;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
**3. src/main/webapp/app1/templates/screen/ljw/Test1.vm
** 下面这段 vm 模板代码有好多个关键点:
- #set ($group = $form.ljw.defaultInstance) 创建一个 group 实例
- #registerMessage($group.id) 这个宏定义在用户输入错误需要返回提示的时候显示出 group.id.message 里面的内容
- $!group.id.value 这个感叹号是用来让 value 忽略 null 错误的
- 粗体部分 ljw_action 会由webx框架转为驼峰风格 LjwAction 并交由这个 Java 类进行表单处理
- event_submit_do_ljw 这个最隐蔽, 粗体部分的下划线风格 do_ljw 会由 webx 框架转为驼峰的 doLjw 并由 LjwAction.doLjw() 处理.
#macro (registerMessage $field)
#if (!$field.valid) $field.message #end
#end
<h2>From Template</h2>
<form action="$app1Link.setTarget("ljw/Test1")" method="post">
$csrfToken.hiddenField
<input type="hidden" name="action" value="ljw_action" />
#set ($group = $form.ljw.defaultInstance)
#registerMessage($group.id)
#registerMessage($group.pwd)
<input type="text" name="$group.id.key" value="$!group.id.value"/>
<input type="password" name="$group.pwd.key" value="$!group.pwd.value"/>
<input type="submit" name="event_submit_do_ljw"/>
#if ($id)
<p> $id </p>
#end
</form>
4. src/main/webapp/WEB-INF/app1/form.xml
与其他的 group 并列, 加入一个新的 group, 这个一看就懂了, 什么 required 的已经自解释了. 当然除了必填项, 还支持各种各样的验证形式, 正则表达什么的, 交给 Java 判断等等. 详见官网文档.
<group name="ljw" extends="csrfCheck">
<field name="id" displayName="你的账号">
<fm-validators:required-validator>
<message>必须填写 ${displayName}</message>
</fm-validators:required-validator>
</field>
<field name="pwd" displayName="你的密码">
<fm-validators:required-validator>
<message>${displayName} 还是要填的</message>
</fm-validators:required-validator>
</field>
</group>