本节书摘来自异步社区《Spring MVC学习指南(第2版)》一书中的第2章,第2.5节,作者:【美】Paul Deck著,更多章节内容可以访问云栖社区“异步社区”公众号查看
在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页面
相关资源:Spring MVC学习指南 高清完整.pdf版下载