Spring MVC 验证表单

   在实际工作中,得到数据后的第一步就是检验数据的正确性,如果存在录入上的问题,一般会通过注解校验,发现错误后返回给用户,但是对于一些逻辑上的错误,比如购买金额=购买数量×单价,这样的规则就很难使用注解方式进行验证了,这个时候可以使用Spring所提供的验证器(Validator)规则去验证。
  所有的验证都是要先注册验证器,不过验证器也是SpringMVC自动加载的。这里笔者下载了关于验证器所需的jar包,包括classmate-1.3.3.jar、jboss-logging-3.3.1.Final.jar、hibernate-validator-5.4.1.Final.jar和validation-api-1.1.0.Final.jar。其中,validation-api-1.1.0. Final.jar提供关于验证注解的,它只有一些定义,而没有实现;hibernate-validator-5.4.1. Final.jar是通过Hibernate检验规则的包,它的运行还依赖于classmate-1.3.3.jar和jboss-logging-3.3.1.Final.jar这两个包。我们使用Hibernate检验规则把这些包加载进来。
<!-- 验证-->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.4.1.Final</version>
</dependency>

 

使用JSR 303注解验证输入内容

  Spring提供了对Bean的功能校验,通过注解@Valid标明哪个Bean需要启用注解式的验证。在javax.validation.con-straints.*中定义了一系列的JSR 303规范给出的注解,在使用它们之前需要对这些注解有一定的了解,如表所示。 Spring MVC 验证表单

 


Spring MVC 验证表单
  代码清单15-35:交易表单
<%@ 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>validate</title>
</head>

<body>
<%--<form action="../validate/annotation.do">--%>
<form action="../validate/validator.do">
    <table>
        <tr>
            <td>产品编号:</td>
            <td><input name="productId" id="productId" value="12"/></td>
        </tr>
        <tr>
            <td>用户编号:</td>
            <td><input name="userId" id="userId" value="1231"/></td>
        </tr>
        <tr>
            <td>交易日期:</td>
            <td><input name="date" id="date" value="2019-06-30"/></td>
        </tr>
        <tr>
            <td>价格:</td>
            <td><input name="price" id="price" value="12323"/></td>
        </tr>
        <tr>
            <td>数量:</td>
            <td><input name="quantity" id="quantity" value="500"/></td>
        </tr>
        <tr>
            <td>交易金额:</td>
            <td><input name="amount" id="amount" value="3523"/></td>
        </tr>
        <tr>
            <td>用户邮件:</td>
            <td><input name="email" id="email" value="email"/></td>
        </tr>
        <tr>
            <td>备注:</td>
            <td><textarea id="note" name="note" cols="20" rows="5">我的世界</textarea></td>
        </tr>
        <tr>
            <td colspan="2" align="right"><input type="submit" value="提交"/>
        </tr>
    </table>
</form>
</body>
</html>

 


  代码清单15-36:表单POJO
package com.ssm.chapter15.pojo;

import org.springframework.format.annotation.DateTimeFormat;

import javax.validation.constraints.*;
import java.util.Date;

public class Transaction {

    // 产品编号
    @NotNull // 不能为空
    private Long productId;

    // 用户编号
    @NotNull // 不能为空
    private Long userId;

    // 交易日期
    @Future // 只能是将来的日期
    @DateTimeFormat(pattern = "yyyy-MM-dd") // 日期格式化转
    @NotNull // 不能为空
    private Date date;


    // 价格
    @NotNull // 不能为空
    @DecimalMin(value = "0.1") // 最小值0.1
    private Double price;

    // 数量
    @Min(1)  // 最小值为1
    @Max(100) // 最大值
    @NotNull // 不能为空
    private Integer quantity;

    // 交易金额
    @NotNull // 不能为空
    @DecimalMax("500000.00") // 最大金额为5万元
    @DecimalMin("1.00") // 最小交易金额1元
    private Double amount;

    // 邮件
    @Pattern( // 正则式
            regexp = "^([a-zA-Z0-9]*[-_]?[a-zA-Z0-9]+)*@" + "([a-zA-Z0-9]*[-_]?[a-zA-Z0-9]+)+[\\.][A-Za-z]{2,3}([\\.] [A-Za-z]{2})?$",
            // 自定义消息提示
            message = "不符合邮件格式")
    private String email;

    // 备注
    @Size(min = 0, max = 256)// 0到255个字
    private String note;

    @NotNull
    public Long getProductId() {
        return productId;
    }

    public void setProductId(@NotNull Long productId) {
        this.productId = productId;
    }

    @NotNull
    public Long getUserId() {
        return userId;
    }

    public void setUserId(@NotNull Long userId) {
        this.userId = userId;
    }

    @NotNull
    public Date getDate() {
        return date;
    }

    public void setDate(@NotNull Date date) {
        this.date = date;
    }

    @NotNull
    public Double getPrice() {
        return price;
    }

    public void setPrice(@NotNull Double price) {
        this.price = price;
    }

    @NotNull
    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(@NotNull Integer quantity) {
        this.quantity = quantity;
    }

    @NotNull
    public Double getAmount() {
        return amount;
    }

    public void setAmount(@NotNull Double amount) {
        this.amount = amount;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }

    @Override
    public String toString() {
        return "Transaction{" +
                "productId=" + productId +
                ", userId=" + userId +
                ", date=" + date +
                ", price=" + price +
                ", quantity=" + quantity +
                ", amount=" + amount +
                ", email='" + email + '\'' +
                ", note='" + note + '\'' +
                '}';
    }
}

 

  代码清单15-37:用控制器验证表单
package com.ssm.chapter15.controller;

import com.ssm.chapter15.pojo.Transaction;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.validation.Valid;
import java.util.List;

@Controller
@RequestMapping("/validate")
public class ValidateController {

    @RequestMapping("/annotation")
    public ModelAndView annotationValidate(@Valid Transaction trans, Errors errors) {

        //是否存在错误
        if (errors.hasErrors()) {
            // 获取错误信息
            List<FieldError> errorList = errors.getFieldErrors();
            for (FieldError error : errorList) {
                //打印字段错误信息
                System.err.println("fied :" + error.getField() + "\t" + "msg:" + error.getDefaultMessage());
            }
        }
        ModelAndView mv = new ModelAndView();
        mv.setViewName("index");
        return mv;
    }
}

 


使用验证器

  有时候除了简单的输入格式、非空性等校验,也需要一定的业务校验,Spring提供了Validator接口来实现检验,它将在进入控制器逻辑之前对参数的合法性进行检验。
  代码清单15-39:交易验证器
package com.ssm.chapter15.validator;

import com.ssm.chapter15.pojo.Transaction;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

public class TransactionValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {

        //判断验证是否为Transaction,如果是则进行验证
        return Transaction.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        Transaction trans = (Transaction) target;
        // 求交易金额和价格×数量的差额
        double dis = trans.getAmount() - (trans.getPrice() * trans.getQuantity());
        //如果差额大于0.01,则认为业务错误
        if (Math.abs(dis) > 0.01) {
            // 加入错误信息
            errors.rejectValue("amount", null, "交易金额和购买数量与价格不匹配");
        }
    }
}

 

  这样这个验证器就判断了是否Transaction对象,如果是才去验证后面的逻辑,那么要将它捆绑到对应的控制器中,这个时候Spring MVC提供了注解@InitBinder,后文我们会再次详细讲解@InitBinder。通过它就可以将验证器和控制器捆绑到一起,这样就能够对请求表单进行验证了。对于@InitBinder的使用还有其他的内容,这里只展示其捆绑验证器的方法。

  代码清单15-40:使用验证器验证
@InitBinder
public void initBinder(DataBinder binder) {

    //数据绑定器加入验证器
    binder.setValidator(new TransactionValidator());
}

@RequestMapping("/validator")
public ModelAndView validator(@Valid Transaction trans, Errors errors) {
    //是否存在错误
    if (errors.hasErrors()) {
        // 获取错误信息
        List<FieldError> errorList = errors.getFieldErrors();
        for (FieldError error : errorList) {
            // 打印字段错误信息
            System.err.println("fied :" + error.getField() + "\t" + "msg:" + error.getDefaultMessage());
        }
    }
    ModelAndView mv = new ModelAndView();
    mv.setViewName("index");
    return mv;
}

 

  比较遗憾的是,JSR 303注解方式和验证器方式不能同时使用,不过可以在使用JSR 303注解方式得到基本的检验信息后,再使用自己的方法进行验证。 
上一篇:Mysql在插入时没有错误或在非空字段上更新为null值


下一篇:Spring方法级别数据校验:@Validated