Spring MVC-07循序渐进之验证器 上 (Spring自带的验证框架)

简介: Spring MVC-07循序渐进之验证器 上 (Spring自带的验证框架)

概述


SpringMVC中有两种方式可以进行验证输入

  • 利用Spring自带的验证框架
  • 利用JSR 303实现

本篇博文我们将分别讲述这两种输入验证方法


验证概览


Converter和Formatter作用域Field级。 在MVC应用程序中,它们将String转换或者格式化成另外一种Java类型,比如java.util.Date.


验证器则作用于object级。它决定某一个对象中的所有field是否均是有效的,以及是否遵循某些规则。


那么,思考一个问题如果一个应用程序中即使用了Formatter也使用了validator ,则他们的事件顺序是怎么的呢?


在调用Controller期间,将会有一个或者多个Formatter,视图将输入字符串转换成domain对象的field值,一旦格式化成功,则验证器就会介入。


Spring验证器


Spring的输入验证甚至早于JSR 303(Java验证规范),尽管对于新的项目,一般建议使用JSR303验证器

为了创建Spring验证器,需要实现org.springframework.validation.Validator接口。

接口源码如下

public interface Validator {
    boolean supports(Class<?> clazz);
    void validate(Object target, Errors errors);
}


supports:验证器可以处理可以处理指定的Class ,supports方法将返回true。 validate方法会验证目标对象,并将验证错误填入Errors对象

Errors对象是org.springframework.validation.Errors接口的一个实例,包含了一系列FieldError和ObjectError对象


编写验证器,不需要直接创建Error对象,因为实例化ObjectError或者FieldError。 大多数时候,只给reject或者rejectValue方法传入一个错误码,Spring就会在属性文件中查找错误码没回去相应的错误消息, 还可以传入一个默认的消息,当没有找到指定的错误码时,就会使用默认消息


Errors对象中的错误消息可以利用表单标签库的Errors标签显示在页面中, 错误消息可以通过Spring支持的国际化特性本地化。


ValidationUtils类


org.springframework.validation.ValidationUtils是一个工具类,有助于编写Spring验证器

方法如下


20180226190632114.png

Spring验证器Demo


2018022619075193.png


这个demo中,我们使用了一个ProductValidator的验证器,用于验证Product对象。

Product类

package com.artisan.domain;
import java.io.Serializable;
import java.util.Date;
public class Product implements Serializable {
    private static final long serialVersionUID = 748392348L;
    private String name;
    private String description;
    private Float price;
    private Date productionDate;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public Float getPrice() {
        return price;
    }
    public void setPrice(Float price) {
        this.price = price;
    }
    public Date getProductionDate() {
        return productionDate;
    }
    public void setProductionDate(Date productionDate) {
        this.productionDate = productionDate;
    }
}


验证器类

package com.artisan.validator;
import java.util.Date;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.artisan.domain.Product;
/**
 * 
 * @ClassName: ProductValidator
 * @Description: 实现Validator接口,对Product进行校验
 * @author Mr.Yang
 * @date 2018年2月26日
 *
 */
public class ProductValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return Product.class.isAssignableFrom(clazz);
    }
    @Override
    public void validate(Object target, Errors errors) {
        // 强制转成校验对象
        Product product = (Product) target;
        // 校验必填字段
        ValidationUtils.rejectIfEmpty(errors, "name", "productname.required");
        ValidationUtils.rejectIfEmpty(errors, "price", "price.required");
        ValidationUtils.rejectIfEmpty(errors, "productionDate", "productiondate.required");
        // 校验price
        Float price = product.getPrice();
        if (price != null && price < 0) {
            errors.rejectValue("price", "price.negative");
        }
        // 校验productionDate
        Date productionDate = product.getProductionDate();
        if (productionDate != null) {
            // The hour,minute,second components of productionDate are 0
            if (productionDate.after(new Date())) {
                errors.rejectValue("productionDate", "productiondate.invalid");
            }
        }
    }
}


ProductValidator是一个非常简单的校验器,它的validate方法校验Product方法是否有名称和价格,且价格不能为负数,它还会确保生产日期不能晚于今天的日期。


源文件


验证器不需要显式注册,但是如果想从某个属性文件中获取错误消息,则需要通过声明messageSourceBean,告诉Spring去哪里查找这个文件

完整的SpringMVC的配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd     
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 扫描控制层的注解,使其成为Spring管理的Bean -->
    <context:component-scan base-package="com.artisan.controller"/>
    <!-- 静态资源文件 -->
    <mvc:annotation-driven  conversion-service="conversionService"/>
    <mvc:resources mapping="/css/**" location="/css/"/>
    <mvc:resources mapping="/*.jsp" location="/"/>
    <!-- 视图解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <bean id="messageSource" 
            class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="/WEB-INF/resource/messages" />
    </bean>
    <bean id="conversionService"
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="formatters">
            <set>
                <bean class="com.artisan.formatter.DateFormatter">
                    <constructor-arg type="java.lang.String" value="MM-dd-yyyy" />
                </bean>
            </set>
        </property>
    </bean>
</beans>

国际化资源文件 messages.properties

productname.required=Please enter a product name
price.required=Please enter a price
price.negative=Please enter a price > 0
productiondate.required=Please enter a production date
productiondate.invalid=Invalid production date. Please ensure the production date is not later than today.


Controller类


在Controller类中通过实例化validator类,就可以使用Spring验证器了。为了校验改验证器是否生成错误的消息,需要找BindingResult中调用hasErrors方法


package com.artisan.controller;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import com.artisan.domain.Product;
import com.artisan.validator.ProductValidator;
@Controller
@RequestMapping("/product")
public class ProductController {
     private static final Logger logger = Logger.getLogger(ProductController.class);
     @RequestMapping(value = "/product_input")
        public String inputProduct(Model model) {
            model.addAttribute("product", new Product());
            return "ProductForm";
        }
        @RequestMapping(value = "/product_save")
        public String saveProduct(@ModelAttribute Product product,
                BindingResult bindingResult, Model model) {
            logger.info("product_save");
            // 校验Product
            ProductValidator productValidator = new ProductValidator();
            productValidator.validate(product, bindingResult);
            if (bindingResult.hasErrors()) {
                FieldError fieldError = bindingResult.getFieldError();
                logger.info("Code:" + fieldError.getCode() + ", field:" + fieldError.getField());
                return "ProductForm";
            }
            // save product here
            model.addAttribute("product", product);
            return "ProductDetails";
        } 
}

使用Spring验证器的第二种方式: 在Controller中编写initBinder方法,并将验证器传到WebDataBinder ,并调用validate方法

@org.springframework.web.bind.annotation.InitBinder
public void initBinder(WebDataBinder binder){
    // this will apply the validator  to all request-handling methods
    binder.setValidator(new ProductValidator());
    binder.validate();
}


将验证器传到WebDataBinder,会使该验证器应用于Controller类中所有请求的方法。


或者利用@javax.validation.Valid对要验证的对象参数进行标注

public String saveProduct(@Valid @ModelAttribute Product product,BindingResult bindingResult,Model model){
}


Valid是JSR303中定义的,下篇博文将介绍。


测试验证器

什么都不输入的情况下


20180226192500976.png


价格输入一个小于0 , 时间输入一个大于今天的日期


20180226192809421.png

输入正确的结果


20180226193048352.png


源码

代码已提交到github

https://github.com/yangshangwei/SpringMvcTutorialArtisan

相关文章
|
11天前
|
JSON 安全 算法
|
6天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
19天前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
34 4
|
16天前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
27 0
|
10天前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
32 1
Spring 框架:Java 开发者的春天
|
3天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
11 2
|
2天前
|
消息中间件 NoSQL Java
springboot整合常用中间件框架案例
该项目是Spring Boot集成整合案例,涵盖多种中间件的使用示例,每个案例项目使用最小依赖,便于直接应用到自己的项目中。包括MyBatis、Redis、MongoDB、MQ、ES等的整合示例。
43 1
|
10天前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。
|
16天前
|
人工智能 开发框架 Java
总计 30 万奖金,Spring AI Alibaba 应用框架挑战赛开赛
Spring AI Alibaba 应用框架挑战赛邀请广大开发者参与开源项目的共建,助力项目快速发展,掌握 AI 应用开发模式。大赛分为《支持 Spring AI Alibaba 应用可视化调试与追踪本地工具》和《基于 Flow 的 AI 编排机制设计与实现》两个赛道,总计 30 万奖金。
|
17天前
|
人工智能 Java API
阿里云开源 AI 应用开发框架:Spring AI Alibaba
近期,阿里云重磅发布了首款面向 Java 开发者的开源 AI 应用开发框架:Spring AI Alibaba(项目 Github 仓库地址:alibaba/spring-ai-alibaba),Spring AI Alibaba 项目基于 Spring AI 构建,是阿里云通义系列模型及服务在 Java AI 应用开发领域的最佳实践,提供高层次的 AI API 抽象与云原生基础设施集成方案,帮助开发者快速构建 AI 应用。本文将详细介绍 Spring AI Alibaba 的核心特性,并通过「智能机票助手」的示例直观的展示 Spring AI Alibaba 开发 AI 应用的便利性。示例源