javax.validation 添加了注释但没有进行验证,返回的 errors 数量为0的解决办法

2 篇文章 0 订阅

今天在学习 Spring 实战 第四版的第五章过程中,使用到了 spring MVC 对 Java 校验 API 的支持,添加了 javax.validation 中的注释来实现对 Spitter 类属性的输入验证。

因为书中并没有详细的代码,加上之前没有进行任何相关的学习,因此只能一步步靠自己摸索出来。

首先贴上 Spitter 类:

package org.fisherman.spittr.pojo;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * Created by fisherman
 * 2018/8/30
 */
public class Spitter {

    private Long id;

    @NotEmpty
    @Size(min = 5, max = 16)
    private String firstName;

    @NotEmpty
    @Size(min = 5, max = 25)
    private String lastName;

    @NotEmpty
    @Size(min = 2, max = 30)
    private String username;

    @NotEmpty
    @Size(min = 2, max = 30)
    private String password;

    public Spitter(Long id, String firstName, String lastName, String username, String password) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.username = username;
        this.password = password;
    }

    public Spitter(String firstName, String lastName, String username, String password) {
        this.id = 0L;
        this.firstName = firstName;
        this.lastName = lastName;
        this.username = username;
        this.password = password;
    }

    public Spitter() {
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Spittle) {
            return EqualsBuilder.reflectionEquals(this, obj, "id");
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this, "id");
    }
}

然后是 SpitterController 类:

package org.fisherman.spittr.web;

import org.fisherman.spittr.data.SpitterRepository;
import org.fisherman.spittr.pojo.Spitter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.validation.Valid;

/**
 * Created by fisherman
 * 2018/8/30
 */
@Controller
@RequestMapping(value = "/spitter")
public class SpitterController {
    private SpitterRepository spitterRepository;

    public SpitterController() {
    }

    @Autowired
    public SpitterController(SpitterRepository spitterRepository) {
        this.spitterRepository = spitterRepository;
    }

    @RequestMapping(value = "/register", method = RequestMethod.GET)
    public String showRegistrationForm() {
        return "registerForm";
    }

    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public String processRegistration(@Valid Spitter spitter,
                                      Errors errors) {
        if (errors.hasErrors()) {
            return "registerForm";
        }
        spitterRepository.save(spitter);
        // redirect 到该新增用户的个人页
        return "redirect:/spitter/" + spitter.getUsername();
    }

    @RequestMapping(value = "/{username}", method = RequestMethod.GET)
    public String showSpitterProfile(@PathVariable String username, Model model) {
        Spitter spitter = spitterRepository.findByUsername(username);
        model.addAttribute(spitter);
        return "profile";
    }
}

重点关注 SpitterController 类中的 processRegistration() 方法,有两点需要注意:

  1. 在需要验证的传入参数,也就是表单提交的参数,Spitter 之前需要加上 @Valid 注释,告知 Spring 需要确保这个对象满足校验限制;
  2. 在 Spitter 属性上添加了校验限制并不能阻止表单提交,因此即使用户没有填写某个域或者某个域给定的值超过了最大长度,processRegistration() 方法依然会被调用,这就要求我们需要处理校验的错误;
  3. 校验出现的错误可以通过 Errors 对象进行访问,Errors 参数需要紧跟在带有 @Valid 注解的参数后面,即跟在需要进行校验的参数后面。

在完成代码初步编写并进行测试时,发现空表单的提交会直接 redirect 到 /spitter/ 这个路径,debug后验证了自己的想法,原因就是 validation 没有作用,errors 的数量为0,因此 errors.hasErrors() 方法返回false,从此开始了寻找解决办法之旅。

在网上搜索时发现出现这种问题的情况貌似不多,因此仔细看书后发现书中提示了一点:在 Spring MVC 中要使用 Java 校验 API的话,不需要什么额外配置,只需要保证在 classpath 下包含这个 Java 校验 API 的实现,比如 Hibernate Validator。

因此在 maven 中添加了 hibernate validator 的依赖,然后在 Spring 的配置文件中需要加入 启用此 validator 的 bean,applicationContext.xml文件如下:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p" 
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd 
       http://www.springframework.org/schema/mvc 
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:annotation-config/>

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

    <context:component-scan base-package="org.fisherman.spittr"/>

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>

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

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/views/" p:suffix=".jsp"/>

</beans>

其中 localValidatorFactoryBean 为生成 validator 的工厂 bean 类,需要注入,然后需要添加<mvc:annotation-driven/>的配置,而且需要制定 validator 为注入的 localValidatorFactoryBean 类。

最后需要注意的一点是,光引入 Hibernate Validator 并不行,还有其所需要的依赖需要引入,共有如下几个:

<!-- validation -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.2.Final</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.jboss.logging</groupId>
    <artifactId>jboss-logging</artifactId>
    <version>3.3.2.Final</version>
</dependency>
<dependency>
    <groupId>com.fasterxml</groupId>
    <artifactId>classmate</artifactId>
    <version>1.3.4</version>
</dependency>

在我的使用过程中,只在 maven 中添加依赖会有其他问题,比如 tomcat 容器无法找到这些类,如果出现此问题,需要将依赖的这些 jar 包全部添加到 tomcat 下的 lib 文件夹下,到此,就不会再有其他问题。

`javax.validation.ConstraintViolationException` 是 Java Bean Validation 规范中定义的异常,表示数据校验失败。当使用注解对方法参数进行校验时,如果参数不符合校验规则,则会抛出 `javax.validation.ConstraintViolationException` 异常。 一般情况下,我们需要在代码中对该异常进行捕获和处理,并将错误信息返回给客户端。以下是一个使用 `@ExceptionHandler` 注解捕获和处理 `javax.validation.ConstraintViolationException` 异常的例子: ```java @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(javax.validation.ConstraintViolationException.class) @ResponseBody public ResponseEntity<String> handleValidationException(javax.validation.ConstraintViolationException ex) { Set<ConstraintViolation<?>> violations = ex.getConstraintViolations(); StringBuilder sb = new StringBuilder(); for (ConstraintViolation<?> violation : violations) { sb.append(violation.getMessage()).append("\n"); } return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(sb.toString()); } } ``` 在上述代码中,我们使用 `@ExceptionHandler` 注解捕获 `javax.validation.ConstraintViolationException` 异常,并通过 `ex.getConstraintViolations()` 方法获取到所有的校验错误信息。然后将错误信息构造成一个字符串返回给客户端。 需要注意的是,使用 `@ControllerAdvice` 注解可以将异常处理器作用于所有的控制器中。而 `@ExceptionHandler` 注解则用于标识具体的异常处理方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值