《Spring MVC学习指南(第2版)》——2.5 校验器

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页面

时间: 2024-09-08 09:55:49

《Spring MVC学习指南(第2版)》——2.5 校验器的相关文章

《Spring MVC学习指南(第2版)》——2.7 小结

2.7 小结 在本章中,我们学习了基于MVC模式的模型2架构以及如何基于servlet控制器或者filter分发器开发一个模型2应用.两个示例分别为appdesign1和appdesign2.使用servlet作为过滤器上的控制器,一个明显的优点是你可以将servlet配置为欢迎页面. 在模型2应用程序中,JSP页面通常用做视图,当然也可以使用其他技术,如Apache Velocity和FreeMarker. 如果JSP页面用做模型2体系结构中的视图,那些页面仅用于显示值,并且不应在其中显示脚本

《Spring MVC学习指南(第2版)》——导读

前言 Spring MVC是Spring框架中用于Web应用快速开发的一个模块.Spring MVC的MVC是Model-View-Controller的缩写.它是一个广泛应用于图形化用户交互开发中的设计模式,不仅常见于Web开发,也广泛应用于如Swing和JavaFX等桌面开发. 作为当今业界最主流的Web开发框架,Spring MVC(有时也称Spring Web MVC)的开发技能相当热门.本书可供想要学习如何通过Spring MVC开发基于Java的Web应用的开发人员阅读. HTTP使

《Spring MVC学习指南(第2版)》——第1章 Spring框架 1.1XML配置文件

第1章 Spring框架 Spring框架是一个开源的企业应用开发框架,作为一个轻量级的解决方案,它包含20多个不同的模块.本书主要关注Core.Spring Bean.Spring MVC和Spring MVC Test模块. 本章主要介绍Core和Spring Bean这两个模块,以及它们如何提供依赖注入解决方案.为方便初学者,本书会深入讨论依赖注入概念的细节.后续介绍开发MVC应用的章节将会使用到本章介绍的技能. 依赖注入 简单来说,依赖注入的情况如下. 有两个组件A和B,A依赖于B.假定

《Spring MVC学习指南(第2版)》——第2章 模型2和MVC模式 2.1模型1介绍

第2章 模型2和MVC模式 Java Web应用开发中有两种设计模型,为了方便,分别称为模型1和模型2.模型1是以页面中心,适合于小应用开发.而模型2基于MVC模式,是Java Web应用的推荐架构(简单类型的应用除外). 本章将会讨论模型2,并展示4个不同示例应用.第一个应用是一个基本的模型2应用,采用Servlet作为控制器:第二个应用也是模型2应用,但采用了Filter作为控制器:第三个应用引入了验证控件来校验用户的输入:最后一个应用则采用了一个自研的依赖注入器.在实践中,应替换为Spri

《Spring MVC学习指南(第2版)》——1.3 小结

1.3 小结 本章学习了依赖注入的概念以及基于Spring容器的实践,后续将在此基础之上配置Spring应用.

《Spring MVC学习指南(第2版)》——1.2 Spring控制反转容器的使用

1.2 Spring控制反转容器的使用 本节主要介绍Spring如何管理bean和依赖关系.1.2.1 通过构造器创建一个bean实例 前面已经介绍,通过调用ApplicationContext的getBean方法可以获取一个bean的实例.下面的配置文件中定义了一个名为product的bean(见清单1.1). 清单1.1 一个简单的配置文件 < ?xml version="1.0" encoding="UTF-8"?> < beans xmln

《Spring MVC学习指南(第2版)》——2.6 依赖注入

2.6 依赖注入 在过去数年间,依赖注入技术作为代码可测试性的一个解决方案已经广泛应用.实际上,Spring.Struts2等伟大框架都采用了依赖注入技术.那么,什么是依赖注入技术? 有两个组件A和B,A依赖于B.假定A是一个类,且A有一个方法importantMethod使用到了B,如下: public class A { public void importantMethod() { B b = ... // get an instance of B b.usefulMethod(); ..

《Spring MVC学习指南(第2版)》——2.3 模型2之Servlet控制器

2.3 模型2之Servlet控制器 为了便于对模型2有一个直观的了解,本节将展示一个简单模型2应用.实践中,模型2的应用非常复杂. 示例应用名为appdesign1,其功能设定为输入一个产品信息.具体为:用户填写产品表单(图2.2)并提交:示例应用保存产品并展示一个完成页面,显示已保存的产品信息(见图2.3). 图2.2 产品表单 图2.3 产品详细页 示例应用支持如下两个action. (1)展示"添加产品"表单.该action将图2.2中的输入表单发送到浏览器上,其对应的URI应

《Spring MVC学习指南(第2版)》——2.4 模型2之Filter分发器

2.4 模型2之Filter分发器 虽然servlet是模型2应用程序中最常见的控制器,但过滤器也可以充当控制器.但请注意,过滤器没有作为欢迎页面的权限.仅输入域名时不会调用过滤器分派器.Struts 2使用过滤器作为控制器,是因为该过滤器也用于提供静态内容. 下面的例子(appdesign2)是一个采用filter分发器的模型2应用,目录结构如图2.5所示. 图2.5 appdesign2目录结构 JSP页面和Product类同appdesign1相同,但没有采用servlet作为控制器,而是