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

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/catoop/article/details/51284638 本文接上一篇《SpringMVC 使用验证框架 Bean Validation(上)》:四、Controller 普通参数验证与视图错误信息的展示对于 form 表单提交绑定到对象的验证方式,上面已经介绍了。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/catoop/article/details/51284638

本文接上一篇《SpringMVC 使用验证框架 Bean Validation(上)》:


四、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;
    }
}
目录
相关文章
|
29天前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
|
2月前
|
XML 前端开发 Java
深入理解SpringMVC工作原理,像大牛一样手写SpringMVC框架
对于SpringMVC相信诸位并不陌生,这是Java开发过程中使用最频繁的框架,在你的项目中可能不一定用MyBatis,但绝对会使用SpringMVC,因为操作数据库还有Hibernate、JPA等其他ORM框架选择,但SpringMVC这个框架在其领域中,可谓是独领风骚
|
4月前
|
前端开发 Java Apache
JAVAEE框架技术之6-springMVC拦截器和文件上传功能
JAVAEE框架技术之6-springMVC拦截器和文件上传功能
94 0
JAVAEE框架技术之6-springMVC拦截器和文件上传功能
|
3月前
|
前端开发 Java 应用服务中间件
Spring框架第六章(SpringMVC概括及基于JDK21与Tomcat10创建SpringMVC程序)
Spring框架第六章(SpringMVC概括及基于JDK21与Tomcat10创建SpringMVC程序)
|
4月前
|
前端开发 Java 应用服务中间件
Spring MVC框架概述
Spring MVC 是一个基于Java的轻量级Web框架,采用MVC设计模型实现请求驱动的松耦合应用开发。框架包括DispatcherServlet、HandlerMapping、Handler、HandlerAdapter、ViewResolver核心组件。DispatcherServlet协调这些组件处理HTTP请求和响应,Controller处理业务逻辑,Model封装数据,View负责渲染。通过注解@Controller、@RequestMapping等简化开发,支持RESTful请求。Spring MVC具有清晰的角色分配、Spring框架集成、多种视图技术支持以及异常处理等优点。
63 1
|
4月前
|
前端开发 JavaScript Java
MVC框架:SpringMVC(三)
MVC框架:SpringMVC
54 0
|
4月前
|
JSON 前端开发 JavaScript
MVC框架:SpringMVC(二)
MVC框架:SpringMVC
59 0
|
4月前
|
前端开发 Java 应用服务中间件
MVC框架:SpringMVC(一)
MVC框架:SpringMVC
91 0
|
4月前
|
前端开发 Java 数据库连接
认识Java中最常用的框架:Spring、Spring MVC、Spring Boot、MyBatis和Netty
Spring框架 Spring是一个轻量级的开源框架,用于构建企业级应用。它提供了广泛的功能,包括依赖注入、面向切面编程、事务管理、消息传递等。Spring的核心思想是控制反转(IoC)和面向切面编程(AOP)。
223 3
|
4月前
|
前端开发 Java 数据库连接
探索Java中最常用的框架:Spring、Spring MVC、Spring Boot、MyBatis和Netty
探索Java中最常用的框架:Spring、Spring MVC、Spring Boot、MyBatis和Netty