SpringMVC 使用验证框架 Bean Validation(上)

SpringMVC 使用验证框架 Bean Validation(上)

对于任何一个应用而言在客户端做的数据有效性验证都不是安全有效的,这时候就要求我们在开发的时候在服务端也对数据的有效性进行验证。 SpringMVC 自身对数据在服务端的校验(Hibernate Validator)有一个比较好的支持,它能将我们提交到服务端的数据按照我们事先的约定进行数据有效性验证,对于不合格的数据信息 SpringMVC 会把它保存在错误对象中(Errors接口的子类),这些错误信息我们也可以通过 SpringMVC 提供的标签(form:errors)在前端JSP页面上进行展示。或者使用拦截器 after 方法对处理错误信息进行处理后传递给页面(我们使用JSON请求的时候就需要这样做)。

本文来介绍,如何在 SpringMVC 中进行 Validator 的使用。

一、添加POM依赖

<!-- Hibernate Validator -->

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-validator</artifactId>

</dependency>

二、配置要验证的实体

public class ValidatorTest {

// message 直接提供错误信息

@NotNull(message = "username 不能为空")

// message 使用 {} 代表错误内容,从 resources 目录下的 ValidationMessages.properties 文件中读取

@Pattern(regexp = "[a-zA-Z0-9_]{5,10}", message = "{user.username.illegal}")

private String username;

@Size(min = 5, max = 10, message = "{password.length.illegal}")

private String password;

// 省略 get\set

}

ValidationMessages.properties 文件内容:

user.username.illegal=用户名格式不正确

password.length.illegal=密码[${validatedValue}]长度必须为{min}到{max}个字符

其中${validatedValue} 用来获取预校验属性的值。

{min} 和 {max} 用来读取 @Size 注解中对应的属性值。

你还可以像 ${max > 1 ? ‘大于1‘ : ‘小于等于1‘} 这样使用el表达式。

另外我们还可以拿到一个java.util.Formatter类型的formatter变量进行格式化:

${formatter.format("%04d", min)}

如果EL表达式不起作用,可以添加如下依赖尝试,如果没有问题请忽略

<dependency>

<groupId>javax.el</groupId>

<artifactId>javax.el-api</artifactId>

<version>2.2.4</version>

<scope>provided</scope>

</dependency>

内置的验证约束注解 
内置的验证约束注解如下表所示(摘自hibernate validator reference):


验证注解


验证的数据类型


说明


@AssertFalse


Boolean,boolean


验证注解的元素值是false


@AssertTrue


Boolean,boolean


验证注解的元素值是true


@NotNull


任意类型


验证注解的元素值不是null


@Null


任意类型


验证注解的元素值是null


@MIN(value=值)


BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型


验证注解的元素值大于等于@Min指定的value值


@MAX(value=值)


和@Min要求一样


验证注解的元素值小于等于@Max指定的value值


@DecimalMin(value=值)


和@Min要求一样


验证注解的元素值大于等于@ DecimalMin指定的value值


@DecimalMax(value=值)


和@Min要求一样


验证注解的元素值小于等于@ DecimalMax指定的value值


@Digits(integer=整数位数, fraction=小数位数)


和@Min要求一样


验证注解的元素值的整数位数和小数位数上限


@Size(min=下限, max=上限)


字符串、Collection、Map、数组等


验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小


@Past


java.util.Date,java.util.Calendar;Joda
Time类库的日期类型


验证注解的元素值(日期类型)比当前时间早


@Future


与@Past要求一样


验证注解的元素值(日期类型)比当前时间晚


@NotBlank


CharSequence子类型


验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格


@Length(min=下限, max=上限)


CharSequence子类型


验证注解的元素值长度在min和max区间内


@NotEmpty


CharSequence子类型、Collection、Map、数组


验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)


@Range(min=最小值, max=最大值)


BigDecimal,BigInteger,CharSequence, byte,
short, int, long等原子类型和包装类型


验证注解的元素值在最小值和最大值之间


@Email(regexp=正则表达式,flag=标志的模式)


CharSequence子类型(如String)


验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式


@Pattern(regexp=正则表达式,flag=标志的模式)


String,任何CharSequence的子类型


验证注解的元素值与指定的正则表达式匹配


@Valid


任何非原子类型


指定递归验证关联的对象;如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加@Valid注解即可级联验证

三、Controller 实体验证与视图错误信息的展示

JSP 页面:

错误信息使用 form:errors 展示,这个标签,必须放在 form:form 中使用。

<%@ page
language="java" pageEncoding="UTF-8"%>

<%@ taglib
prefix="form" uri="http://www.springframework.org/tags/form" %>

<!DOCTYPE
HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<title>Java验证框架测试</title>

</head>

<body>

<form:form method="post" modelAttribute="testModel" action="${pageContext.request.contextPath }/validator/test3">

<h1><form:errors path="username" /></h1><!-- path的值可以为 * ,表示显示所有错误 -->

<h1><form:errors path="password" /></h1>

<form:input path="username" /><br/>

<form:input path="password" /><br/>

<input type="submit" value="提交"/>

</form:form>

</body>

</html>

对应的 Controller 的方法:

@Controller

@RequestMapping("/validator")

public class ValidatorController {

/**

* 响应到JSP页面

*

* @param test

* @param result

*     
这里的BindingResult必须紧挨着@Valid参数的,即必须紧挨着需要校验的参数,

*     
这就意味着我们有多少个@Valid参数就需要有多少个对应的Errors参数,它们是一一对应的。

* @return

* @author SHANHY

* @create  2016年4月14日

*/

@RequestMapping("/test3")

public String test3(@Valid @ModelAttribute("testModel") ValidatorTest test,

BindingResult result, Model model){

model.addAttribute("test", test);

if(result.hasErrors())

return "validator1";

return "validator2";

}

}


四、Controller 普通参数验证与视图错误信息的展示

对于 form 表单提交绑定到对象的验证方式,上面已经介绍了。但是在很多时候,我们是通过普通传参来调用接口的。 
比如:http://localhost:8080/myproject/hello?name=Shanhy&age=27&password=pwd 
那么对于这种情况,我们该如何校验 name、age、password 的值呢?

看下面的 Controller 代码:

五、JSON 请求响应错误信息

package org.springboot.sample.interceptor;

import java.util.HashMap;

import java.util.Map;

import java.util.Map.Entry;

import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.validation.ConstraintViolation;

import javax.validation.ConstraintViolationException;

import org.springframework.validation.BindingResult;

import org.springframework.validation.FieldError;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.method.HandlerMethod;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

/**

* JSON请求响应错误消息处理

*

* @author 单红宇(365384722)

* @myblog http://blog.csdn.net/catoop/

* @create 2016年4月17日

*/

public class JsonErrorMsgInterceptor implements HandlerInterceptor {

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

throws Exception {

return true;

}

@Override

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,

ModelAndView modelAndView) throws Exception {

if(modelAndView == null)

return;

// 因为MappingJackson2JsonView默认会把BindingResult全部过滤掉。

// 所以我们要想将错误消息输出,要在这里自己处理好。

// 判断请求是否是.json、方法上是否有@ResponseBody注解,或者类上面是否有@RestController注解

// 表示为json请求

if (!request.getRequestURI().endsWith(".json")) {

HandlerMethod handlerMethod =
(HandlerMethod)handler;

if(handlerMethod.getMethodAnnotation(ResponseBody.class)
== null){

if(handlerMethod.getBeanType().getAnnotation(RestController.class)
== null){

return;

}

}

}

Map<String, Object> modelMap = modelAndView.getModel();

if (modelMap != null) {

Map<String, String> errorMsg
= null;

if(modelMap.containsKey("errorMsg")){

errorMsg = (Map<String,
String>)modelMap.get("errorMsg");

}

if(errorMsg == null){

errorMsg = new HashMap<>();

modelMap.put("errorMsg", errorMsg);

}

for (Entry<String, Object> entry :
modelMap.entrySet()) {

if (entry.getValue() instanceof BindingResult) {

BindingResult bindingResult
= (BindingResult) entry.getValue();

if (bindingResult.hasErrors()) {

for (FieldError fieldError :
bindingResult.getFieldErrors()) {

errorMsg.put(fieldError.getObjectName()
+ "." + fieldError.getField(),

fieldError.getDefaultMessage());

}

}

}

}

}

}

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex)

throws Exception {

}

}

注意配置该拦截器配置,使其生效。

效果:

六、错误信息的配置文件

默认在 ValidationMessages.properties 配置文件中(参考 《SpringMVC 使用验证框架 Bean Validation(上)》中的第二点有样例)。

如果想统一使用 messaeg.properties 配置文件中的配置,下面的配置提供参考:

<!-- 指定自己定义的validator -->

<mvc:annotation-driven validator="validator"/>

<!-- 以下 validator  ConversionService 在使用 mvc:annotation-driven 会 自动注册-->

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">

<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>

<!-- 如果不加默认到 使用classpath下的 ValidationMessages.properties -->

<property name="validationMessageSource" ref="messageSource"/>

</bean>

<!-- 国际化的消息资源文件(本系统中主要用于显示/错误消息定制) -->

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">

<property name="basenames">

<list>

<!-- 在web环境中一定要定位到classpath 否则默认到当前web应用下找  -->

<value>classpath:messages</value>

<value>classpath:org/hibernate/validator/ValidationMessages</value>

</list>

</property>

<property name="useCodeAsDefaultMessage" value="false"/>

<property name="defaultEncoding" value="UTF-8"/>

<property name="cacheSeconds" value="60"/>

</bean>

七、错误信息中使用EL表达式

请参考 《SpringMVC 使用验证框架 Bean Validation(上)》中的第二点。

八、方法级别的验证

首先注册 MethodValidationPostProcessor

<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">

<!-- 可以引用自己的 validator 配置,在本文中(下面)可以找到 validator 的参考配置,如果不指定则系统使用默认的 -->

<property name="validator" ref="validator"/>

</bean>

某个 Service 的方法:

@Validated // 告诉MethodValidationPostProcessor此Bean需要开启方法级别验证支持

@Service

public class ValidatorTestService {

public @Length(min = 12, max = 16, message = "返回值长度应该为12-16")

String getContent(

@NotBlank(message = "name不能为空")

String name,

@Size(min = 5, max = 10, message="{password.length.illegal}")

String password) {

return name + ":" + password;

}

}

在 Controller 调用该方法测试:

/**

* 测试方法级别的验证(如果验证失败,则会抛出异常 ConstraintViolationException)

*

* @param name

* @param model

* @return

* @author SHANHY

* @create  2016年4月17日

*/

@RequestMapping("/test5")

@ResponseBody

public Model test5(String name, String password, Model model){

try {

String content =
validatorTestService.getContent(name, password);

model.addAttribute("name", content);

} catch (ConstraintViolationException e) {

addErrorMessage(model, e);

}

return model;

}

/**

* 添加错误消息,建议将该方法提取为一个公共的方法使用。

*

* @param model

* @param e

* @author SHANHY

* @create  2016年5月4日

*/

protected void addErrorMessage(Model model, ConstraintViolationException e){

Map<String, String> errorMsg = new HashMap<>();

model.addAttribute("errorMsg", errorMsg);

for (ConstraintViolation<?>
constraintViolation : e.getConstraintViolations()) {

// 获得验证失败的类 constraintViolation.getLeafBean()

// 获得验证失败的值 constraintViolation.getInvalidValue()

// 获取参数值 constraintViolation.getExecutableParameters()

// 获得返回值 constraintViolation.getExecutableReturnValue()

errorMsg.put(constraintViolation.getLeafBean().getClass().getName()
+ "-" + constraintViolation.getPropertyPath().toString(),
constraintViolation.getMessage());

}

}


工具类

最后提供一个校验工具类,注意看第一个方法就行了:

/**

* Copyright (c) 2005-2012 springside.org.cn

*/

package org.springboot.sample.util;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.Set;

import javax.validation.ConstraintViolation;

import javax.validation.ConstraintViolationException;

import javax.validation.Validator;

/**

* JSR303 Validator(Hibernate Validator)工具类.

*

* ConstraintViolation中包含propertyPath, message 和invalidValue等信息.

* 提供了各种convert方法,适合不同的i18n需求:

* 1. List<String>, String内容为message

* 2. List<String>, String内容为propertyPath + separator + message

* 3. Map<propertyPath, message>

*

* 详情见wiki:
https://github.com/springside/springside4/wiki/HibernateValidator

*/

public class BeanValidatorUtils {

/**

* 调用JSR303的validate方法, 验证失败时抛出ConstraintViolationException.

*

* 参数 Validator 可以直接注入,如:

*

* @Autowired

* protected Validator validator;

*

*/

public static void validateWithException(Validator validator, Object object, Class<?>... groups)

throws ConstraintViolationException {

Set<? extends
ConstraintViolation<?>> constraintViolations =
validator.validate(object, groups);

if (!constraintViolations.isEmpty()) {

throw new
ConstraintViolationException(constraintViolations);

// 调用处捕获异常后,获取错误信息的方法

// List<String> list =
BeanValidatorUtils.extractPropertyAndMessageAsList(ex, ": ");

}

}

/**

* 辅助方法, 转换ConstraintViolationException中的Set<ConstraintViolations>中为List<message>.

*/

public static List<String> extractMessage(ConstraintViolationException e) {

return
extractMessage(e.getConstraintViolations());

}

/**

* 辅助方法, 转换Set<ConstraintViolation>为List<message>

*/

@SuppressWarnings("rawtypes")

public static List<String> extractMessage(Set<? extends ConstraintViolation> constraintViolations) {

List<String> errorMessages = new ArrayList<String>();

for (ConstraintViolation violation :
constraintViolations) {

errorMessages.add(violation.getMessage());

}

return errorMessages;

}

/**

* 辅助方法, 转换ConstraintViolationException中的Set<ConstraintViolations>为Map<property, message>.

*/

public static Map<String, String> extractPropertyAndMessage(ConstraintViolationException e) {

return
extractPropertyAndMessage(e.getConstraintViolations());

}

/**

* 辅助方法, 转换Set<ConstraintViolation>为Map<property, message>.

*/

@SuppressWarnings("rawtypes")

public static Map<String, String> extractPropertyAndMessage(Set<? extends ConstraintViolation> constraintViolations) {

Map<String, String> errorMessages
= new HashMap<String, String>();

for (ConstraintViolation violation :
constraintViolations) {

errorMessages.put(violation.getPropertyPath().toString(),
violation.getMessage());

}

return errorMessages;

}

/**

* 辅助方法, 转换ConstraintViolationException中的Set<ConstraintViolations>为List<propertyPath message>.

*/

public static List<String> extractPropertyAndMessageAsList(ConstraintViolationException e) {

return
extractPropertyAndMessageAsList(e.getConstraintViolations(), " ");

}

/**

* 辅助方法, 转换Set<ConstraintViolations>为List<propertyPath
message>.

*/

@SuppressWarnings("rawtypes")

public static List<String> extractPropertyAndMessageAsList(Set<? extends ConstraintViolation> constraintViolations) {

return
extractPropertyAndMessageAsList(constraintViolations, " ");

}

/**

* 辅助方法, 转换ConstraintViolationException中的Set<ConstraintViolations>为List<propertyPath +separator+ message>.

*/

public static List<String> extractPropertyAndMessageAsList(ConstraintViolationException e, String separator) {

return extractPropertyAndMessageAsList(e.getConstraintViolations(),
separator);

}

/**

* 辅助方法, 转换Set<ConstraintViolation>为List<propertyPath +separator+
message>.

*/

@SuppressWarnings("rawtypes")

public static List<String> extractPropertyAndMessageAsList(Set<? extends ConstraintViolation> constraintViolations,

String separator) {

List<String> errorMessages = new ArrayList<String>();

for (ConstraintViolation violation :
constraintViolations) {

errorMessages.add(violation.getPropertyPath()
+ separator + violation.getMessage());

}

return errorMessages;

}

}

原文地址:https://www.cnblogs.com/wangchaonan/p/10731413.html

时间: 2024-07-30 01:14:29

SpringMVC 使用验证框架 Bean Validation(上)的相关文章

Spring3.1 对Bean Validation规范的新支持(方法级别验证)

一.Bean Validation框架简介 写道 Bean Validation standardizes constraint definition, declaration and validation for the Java platform. 大体意思是:Bean Validation 标准化了Java平台的约束定义.描述.和验证. 详细了解请参考:http://beanvalidation.org/ Bean Validation现在一个有两个规范: 1.Bean Validatio

数据验证框架 Apache BVal 简介

Apache BVal (源码)是实体数据验证 Java Bean Validation 的参考实现.Apache BVal 提供了 JSR 303 规范中所有内置 constraint 的实现,用于对 Bean 中的字段的值进行约束定义.描述和验证.若说 JSR 规范大渣可能还不清楚,但做过 POJO 的 Hibernate Validator 注解的朋友就知道是啥,--那为什么不使用主流的 Hibernate Validator 呢?因为单纯一个压缩包都已经 13mb 了(尽管可以有文档.源

ASP.NET MVC验证框架中关于属性标记的通用扩展方法

http://www.cnblogs.com/wlb/archive/2009/12/01/1614209.html 之前写过一篇文章<ASP.NET MVC中的验证>,唯一的遗憾就是在使用Data Annotation Validators方式验证的时候,如果数据库是Entityframework等自动生成的文件,就没有办法使用扩展属性标记进行标记.现在已经开始有了一些其它的Asp.net MVC 验证框架,使用上跟Data Annotation Validators差不太多,但是普遍有这样

JSR303 Bean Validation 技术规范特性概述

概述 Bean Validation 规范 Bean 是 Java Bean 的缩写,在 Java 分层架构的实际应用中,从表示层到持久化层,每一层都需要对 Java Bean 进行业务符合性验证,如图 1 所示.然而对于同一个 Java Bean 的对象,在每一层都需要实现同样的验证逻辑时,这将是一项耗时且容易诱发错误的做法.Bean Validation 规范的目标就是避免多层验证的重复性.事实上,开发者更倾向于将验证规则直接放到 Java Bean 本身,使用注解的方式进行验证规则的设计.

ssm框架实现图片上传显示并保存地址到数据库

本案例是通过springmvc+spring+mybatis框架以商品上传为例,实现的图片上传功能,并把图片的地址保存到数据库并在前台显示上传的图片. 本项目是使用maven搭建的项目,首先看下项目结构 相关配置自行搜索,下边直接实现上传功能 1.创建数据库 DROP TABLE IF EXISTS `product`; CREATE TABLE `product` ( `pid` int(11) NOT NULL AUTO_INCREMENT, `pimage` varchar(255) DE

SpringMVC验证框架Validation自定义注解实现传递参数到国际化资源文件

关于SpringMVC验证框架Validation的使用方法,不是本篇的重点,可参见博文SpringMVC介绍之Validation 在使用Validation时,一定有朋友遇到过一个问题,那就是:无法传递参数到国际化资源文件properties错误描述中. 举个例子: User类中 @NotEmpty(message="{password.empty.error}") private String password; 资源文件validation_zh_CN.properties中为

Java参数验证Bean Validation 框架

1.为什么要做参数校验? 参数校验和业务逻辑代码分离,参数校验代码复用,统一参数校验方式.校验不太通过时统一异常描述. 2.bean validation规范 JSR303 规范(Bean Validation 规范)提供了对 Java EE 和 Java SE 中的 Java Bean 进行验证的方式.该规范主要使用注解的方式来实现对 Java Bean 的验证功能,并且这种方式会覆盖使用 XML 形式的验证描述符,从而使验证逻辑从业务代码中分离出来.JSR303注解如下: Hibernate

Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC

Bean Validation 1.1当前实现是Hibernate validator 5,且spring4才支持.接下来我们从以下几个方法讲解Bean Validation 1.1,当然不一定是新特性: 集成Bean Validation 1.1到SpringMVC 分组验证.分组顺序及级联验证 消息中使用EL表达式 方法参数/返回值验证 自定义验证规则 类级别验证器 脚本验证器 cross-parameter,跨参数验证 混合类级别验证器和跨参数验证器 组合多个验证注解 本地化 因为大多数时

JSR-303 Bean Validation 介绍及 Spring MVC 服务端验证最佳实践

任何时候,当要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情. 应用程序必须通过某种手段来确保输入参数在上下文来说是正确的. 分层的应用在很多时候,同样的数据验证逻辑会出现在不同的层,这样就会导致代码冗余和一些管理的问题. 为了避免这样或那样的情况发生,最好是将验证逻辑与相应的数据模型进行绑定. 1. JSR-303 Bean Validation JSR 是Java Specification Requests 的缩写,是指向 JCP(Java Community Proces