- 内容提要
- 作者简介
- 译者简介
- 前言
- HTTP
- Servlet 和 JSP
- 下载 Spring 或使用 STS 与 Maven/Gradle
- 手动下载 Spring
- 使用 STS 和 Maven/Gradle
- 下载 Spring 源码
- 本书内容简介
- 下载示例应用
- 第 1 章Spring 框架
- 第 2 章模型 2 和 MVC 模式
- 第 3 章Spring MVC 介绍
- 第 4 章基于注解的控制器
- 第 5 章数据绑定和表单标签库
- 第 6 章转换器和格式化
- 第 7 章验证器
- 第 8 章表达式语言
- 第 9 章JSTL
- 第 10 章国际化
- 第 11 章上传文件
- 第 12 章下载文件
- 第 13 章应用测试
- 附录 A Tomcat
- 附录 B Spring Tool Suite 和 Maven
- 附录 C Servlet
- 附录 D JavaServer Pages
- 附录 E 部署描述符
2.5 校验器
在 Web 应用执行 action 时,很重要的一个步骤就是进行输入校验。校验的内容可以是简单的,如检查一个输入是否为空,也可以是复杂的,如校验信用卡号。实际上,因为校验工作如此重要,Java 社区专门发布了 JSR 303 Bean Validation 以及 JSR 349 Bean Validation 1.1 版本,将 Java 世界的输入检验进行标准化。现代的 MVC 框架通常同时支持编程式和声明式两种校验方法。在编程式中,需要通过编码进行用户输入校验,而在声明式中,则需要提供包含校验规则的 XML 文档或者属性文件。
注意
即使您可以使用 HTML5 或 JavaScript 执行客户端输入验证,也不要依赖它,因为精明的用户可以轻松地绕过它。始终执行服务器端输入验证!
本节的新应用(appdesign3)扩展自 appdesign1,但多了一个 ProductValidator 类(见清单 2.8)。
清单 2.8 ProductValidator 类
package appdesign3.validator;
import java.util.ArrayList;
import java.util.List;
import appdesign3.form.ProductForm;
public class ProductValidator {
public List<String> validate(ProductForm productForm) {
List<String> errors = new ArrayList< >();
String name = productForm.getName();
if (name == null || name.trim().isEmpty()) {
errors.add("Product must have a name");
}
String price = productForm.getPrice();
if (price == null || price.trim().isEmpty()) {
errors.add("Product must have a price");
} else {
try {
Float.parseFloat(price);
} catch (NumberFormatException e) {
errors.add("Invalid price value");
}
}
return errors;
}
}
注意
ProductValidator 类中有一个操作 ProductForm 对象的 validate 方法,确保产品的名字非空,其价格是一个合理的数字。validate 方法返回一个包含错误信息的字符串列表,若返回一个空列表,则表示输入合法。
现在需要让控制器使用这个校验器了,清单 2.9 展示了一个更新后的 ControllerServlet,注意黑体部分。
清单 2.9 新版的 ControllerServlet 类
package appdesign3.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import appdesign3.action.SaveProductAction;
import appdesign3.form.ProductForm;
import appdesign3.model.Product;
import appdesign3.validator.ProductValidator;
import java.math.BigDecimal;
@WebServlet(name = "ControllerServlet", urlPatterns = {
"/input-product", "/save-product"})
public class ControllerServlet extends HttpServlet {
private static final long serialVersionUID = 98279L;
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
@Override
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
private void process(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
String uri = request.getRequestURI();
/*
* uri is in this form: /contextName/resourceName,
* for example: /appdesign1/input-product.
* However, in the case of a default context, the
* context name is empty, and uri has this form
* /resourceName, e.g.: /input-product
*/
int lastIndex = uri.lastIndexOf("/");
String action = uri.substring(lastIndex + 1);
String dispatchUrl = null;
if ("input-product".eauals(action)) {
// no action class, Hrele is nathing to be done
dispatchUrl = "/jsp/ProductForm.jsp";
} else if ("save-product"-eaoals(action)) {
// instantiatle action class
ProductForm productForm = new ProductForm();
// populate action properties
productForm.setName(
request.getParameter("name"));
productForm.setDescription(
request.getParameter("description"));
productForm.setPrice(request.getParameter("price"));
// validate ProductForm
ProductValidator productValidator = new ProductValidator(); List< String> errors = productValidator.validate(productForm); if(errors.isEmpty()){ // create product from productForm Product product = new Product(); product.setName(productForm.getName()); product.setDescription( productForm.getDescription()); product.setPrice(new BigDecimal (productForm.getPrice())); // no validation error execute action method SaveProductAction saveProductAction = new SaveProductAction(); saveProductAction.save(product); // store model in a scope variable for the view request.setAttribute("product", product); dispatchUrl = "/jsp/ProductDetails.jsp"; } else { request.setAttribute("errors", errors); request.setAttribute("form", productForm); dispatchUrl = "/jsp/ProductForm.jsp"; } } // forward to a new if (dispatchUrl != null) { RequestDispatcher rd = request.getRequestDispatcher(dispatchUrl); rd.forward(request, response); } } }
新版的 ControllerServlet 类添加了初始化 ProductValidator 类并调用其 validate 方法的代码。
// validate ProductForm
ProductValidator productValidator = new
ProductValidator();
List<String> errors =
productValidator.validate(productForm);
validate 方法接受一个 ProductForm 参数,它封装了输入到 HTML 表单的产品信息。如果不用 ProductForm,则应将 ServletRequest 传递给验证器。
如果验证成功,validate 方法返回一个空列表,在这种情况下,将创建一个产品并传递给 SaveProductAction,然后,控制器将 Product 存储在 ServletContext 中,并转发到 ProductDetails.jsp 页面,显示产品的详细信息。如果验证失败,控制器将错误列表和 ProductForm 存储在 ServletContext 中,并返回到 ProductForm.jsp。
if (errors.isEmpty()) {
// create Product from ProductForm
Product product = new Product();
product.setName(productForm.getName());
product.setDescription(
productForm.getDescription());
product.setPrice(new BigDecimal(productForm.getPrice()));
// no validation error, execute action method
SaveProductAction saveProductAction = new
SaveProductAction();
saveProductAction.save(product);
// store action in a scope variable for the view
request.setAttribute("product", product);
dispatchur1="/jsp/ProductDetails.jsp";
} else {
request.setAttribute("errors", errors);
request.setAttribute("form", productForm);
dispatchur1="/jsp/ProductForm.jsp";
}
现在,需要修改 appdesign3 应用的 ProductForm.jsp 页面(见清单 2.10),使其可以显示错误信息以及错误的输入。
清单 2.10 ProductForm.jsp 页面
< !DOCTYPE html>
< html>
< head>
< title>Add Product Form< /title>
< style type="text/css">@import url(css/main.css);< /style>
< /head>
< body>
< form method="post" action="save-product">
< h1>Add Product
< span>Please use this form to enter product details< /span>
< /h1>
${empty requestScope.errors? "" : "< p style='color:red'>"
+= "Error(s)!" += "< ul>"} < !--${requestScope.errors.stream().map( x -> "-->< li>"+=x+="< /li>< !--").toList()}--> ${empty requestScope.errors? "" : "< /ul>< /p>"} < label> < span>Product Name :< /span> < input id="name" type="text" name="name" placeholder="The complete product name" value="${form.name}"/> < /label> < label> < span>Description :< /span> < input id="description" type="text" name="description" placeholder="Product description" value="${form.description}"/> < /label> < label> < span>Price :< /span> < input id="price" name="price" type="number" step="any" placeholder="Product price in #.## format" value="${form.price}"/> < /label> < label> < span> < /span> < input type="submit"/> < /label> < /form> < /body> < /html>
现在访问 input-product,测试 appdesign3 应用。
http://localhost:8080/appdesgin3/input-product
若产品表单提交了无效数据,页面将显示相应的错误信息。图 2.6 显示了包含两条错误信息的 ProductForm 页面。
图 2.6 包含两条错误信息的 ProductForm 页面
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论