数据验证是MVC框架中一个重要部分。
大体过程为:
从UI层收集数据,传送到ActionHandler层,在正式调用逻辑处理之前会首先校验,如果校验合格则进入业务处理,否则会将数据校验失败的部分封装成Error Message,存到Request中,ActionHandler返回到原始UI界面,UI界面获取Error Message显示校验失败的部分。根据UI所在地域的不同,从ActionHandler返回的Error Message需要以不同的语言显示,这就是Error Message的国际化显示。
过程分解:
1. 用户在表单内填写数据,点击提交。
2.请求被传送到总控制器。
3.总控制器找到ActionHandler。
4.调用ActionHandler中的验证模板方法进行参数校验。
5.如果校验成功,进入ActionHandler的execute方法;
6.如果校验失败,识别出校验失败项,从资源文件中读取失败提示消息,将消息封装到Message对象,存入Request。控制流跳转到原始UI界面,将消息渲染。
我们从5,6步开始。校验。
因为校验工作是每个ActionHandler中都需要处理的,因此从ActionHandler入手,将验证,执行业务逻辑写成模板方法,这样每一个具体的ActionHandler就可以根据自己的需求来重写验证逻辑,形成一套固定的处理流程。
有关BaseActionHandler中final方法的使用目的,可以参考Java final 关键字。
public abstract class BaseActionHandler {
public abstract boolean validate(HttpServletRequest request, HttpServletResponse response);
public abstract void handleInvalidValidation(HttpServletRequest request, HttpServletResponse response);
public abstract void execute(HttpServletRequest request, HttpServletResponse response);
public final void _execute(HttpServletRequest request, HttpServletResponse response) {
if (this.validate(request, response)) {
this.execute(request, response);
} else {
this.handleInvalidValidation(request, response);
}
}
}
public class DemoActionHandler extends BaseActionHandler {
@Override
public boolean validate(HttpServletRequest request, HttpServletResponse response) {
String title = request.getParameter("title");
String content = request.getParameter("content");
if (title == null || title.equals("")) {
return false;
}
if (content == null || content.equals("")) {
return false;
}
return true;
}
@Override
public void handleInvalidValidation(HttpServletRequest request, HttpServletResponse response) {
String baseName = "org/wales/message/validate";
ResourceBundle resourceBundle = ResourceBundle.getBundle(baseName, Locale.ENGLISH);
String errorMsg = resourceBundle.getString("validation.failed");
request.setAttribute("errors", errorMsg);
try {
request.getRequestDispatcher("/index.jsp").forward(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) {
request.setAttribute("message", "Hi Wales");
try {
request.getRequestDispatcher("/home.jsp").forward(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
关于数据验证,一个是Validate,一个是验证失败之后的handleInvalidValidation。
先来看验证的过程。
最直接的方法就是获取HttpServletRequest,从request中挨个找出参数,然后用逻辑验证参数是否符合业务需求。
@Override
public boolean validate(HttpServletRequest request, HttpServletResponse response) {
String title = request.getParameter("title");
String content = request.getParameter("content");
if (title == null || title.equals("")) {
return false;
}
if (content == null || content.equals("")) {
return false;
}
return true;
}
原始的验证虽然可行,但取值验证的工作量太大,在不用任何MVC框架之前, Commons Validator 是个不错的选择。
如果用了流行的MVC框架,Stru2或者Spring MVC,这些验证工作就简单的多,有关Spring MVC的验证,我们后续分解。
如何处理handleInvalidValidation?
面对验证失败,最简单的处理方法是在界面上显示一个笼统的验证失败信息,高级的则是能明确具体哪一个项目验证失败,然后配上验证失败的提示信息。
我们实现最简单的情况:
@Override
public void handleInvalidValidation(HttpServletRequest request, HttpServletResponse response) {
String baseName = "org/wales/message/validate";
ResourceBundle resourceBundle = ResourceBundle.getBundle(baseName, Locale.ENGLISH);
String errorMsg = resourceBundle.getString("validation.failed");
request.setAttribute("errors", errorMsg);
try {
request.getRequestDispatcher("/index.jsp").forward(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
将验证失败的error message存入request,这样就能在原始的表单页面中读取errors参数,如果该参数有值,则显示error message。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Demo</title>
</head>
<body>
<%
if(request.getAttribute("errors") != null){
String errorMsg = request.getAttribute("errors").toString();
%>
<%= errorMsg %>
<%
}
%>
<form action="/demo/controller" method="post">
Title:<input type="text" name="title" value="please input title" /><br/>
Content:<input type="text" name="content" value="please input content" /><br/>
<input type="submit" name="submit" value="submit" />
</form>
</body>
</html>
以上我们用最原始的方法构建了一个具有验证模块的处理框架,用Spring MVC中处理流程类似,只是验证部分被高度封装了。下方是Spring MVC的样例。
@RequestMapping(value = "/team", method = RequestMethod.POST)
public String createTeam(@Valid @ModelAttribute("team") Team team, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "team/create";
}
database.put("key-" + team.getTeamId(), team);
model.addAttribute("teams", database);
return "team/teams";
}
@Controller
public class ValidationController {
// enforcement of constraints on the JavaBean arg require a JSR-303 provider on the classpath
@RequestMapping("/validate")
public @ResponseBody String validate(@Valid JavaBean bean, BindingResult result) {
if (result.hasErrors()) {
return "Object has validation errors";
} else {
return "No errors";
}
}
}