使用@Valid+BindingResult进行controller参数校验

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用@Valid+BindingResult进行controller参数校验相关的知识,希望对你有一定的参考价值。

参考技术A @Valid注解用于校验,所属的包: javax.validation.Valid.

你可以定义实体,在实体的属性上添加校验规则,在API接收数据时添加@Valid注解,这时你的实体将会开启一个校验的功能。

## 空检查

@NotEmpty: 用在集合类上面;不能为null,而且长度必须大于0 

@NotBlank: 用在String上面;只能作用在String上,不能为null,而且调用trim()后,长度必须大于0 

@NotNull: 用在基本类型上;不能为null,但可以为empty。

## 长度检查

@Size(min=,max=): 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  

@Length(min=, max=) : 只适用于String 类型

##  Booelan检查

@AssertTrue :   验证 Boolean 对象是否为 true  

@AssertFalse :  验证 Boolean 对象是否为 false 

##  日期检查

@Past:  验证 Date 和 Calendar 对象是否在当前时间之前  

@Future:  验证 Date 和 Calendar 对象是否在当前时间之后  

@Pattern:  验证 String 对象是否符合正则表达式的规则

##  数值检查

@Min:   验证 Number 和 String 对象是否大等于指定的值  

@Max:  验证 Number 和 String 对象是否小等于指定的值  

@DecimalMax:   被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度

@DecimalMin: 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度

@Digits:   验证 Number 和 String 的构成是否合法  

@Digits(integer=,fraction=):   验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。

@Valid 和 BindingResult 是一一对应的,如果有多个@Valid,那么每个@Valid后面跟着的BindingResult就是这个@Valid的验证结果,顺序不能乱

1,首先在参数实体类上面加上相关的验证信息

2,controller层

完毕~

带有 BindingResult 的 @Valid JSON 请求导致 IllegalStateException

【中文标题】带有 BindingResult 的 @Valid JSON 请求导致 IllegalStateException【英文标题】:@Valid JSON request with BindingResult causes IllegalStateException 【发布时间】:2012-02-18 16:31:26 【问题描述】:

我有一个接受 JSON 请求的 REST 服务。我想验证传入的 JSON 请求值。我该怎么做?

在 Spring 3.1.0 RELEASE 中,我知道有人想确保他们使用3.1.13 New HandlerMethod-based Support Classes For Annotated Controller Processing 列出的最新支持类

旧的项目是:AnnotationMethodHandlerAdapter。我想确保我使用的是最新版本,例如 RequestMappingHandlerAdapter

这是因为我希望它能解决我看到的问题:

java.lang.IllegalStateException:在没有前面的模型属性的情况下声明了错误/BindingResult 参数。检查您的处理程序方法签名!

我的@Controller处理方法和相关代码是这样的:

@Autowired FooValidator fooValidator;

@RequestMapping(value="/somepath/foo", method=RequestMethod.POST)
public @ResponseBody Map<String, String> fooBar(
        @Valid @RequestBody Map<String, String> specificRequest,
        BindingResult results) 

    out("fooBar called");

    // get vin from JSON (reportRequest)

    return null;



@InitBinder("specificRequest") // possible to leave off for global behavior
protected void initBinder(WebDataBinder binder)
    binder.setValidator(fooValidator);

FooValidator 看起来像这样:

@Component
public class FooValidator  implements Validator 

    public boolean supports(Class<?> clazz) 
        out("supports called ");
        return Map.class.equals(clazz);
    

    public void validate(Object target, Errors errors) 
        out("validate called ");
    


    private void out(String msg) 
        System.out.println("****** " + getClass().getName() + ": " + msg);
    

如果我删除 BindingResult,一切正常,但我无法判断 JSON 是否经过验证。

对于 JSON 请求使用 Map&lt;String, String&gt; 或使用单独的验证器而不是带有验证注释的自定义 Bean(您如何为 JSON 请求执行此操作?)的概念,我并不强烈依赖。任何可以验证 JSON 请求的东西。

【问题讨论】:

我们认为,如果我们能够以某种方式确保我们使用的是较新的类,例如 RequestMappingHandlerAdapter,那么这可能会起作用。你是如何设置的,或者这个问题还有其他好的解决方案吗? 【参考方案1】:

3.1.17 @Valid On @RequestBody Controller Method Arguments 说:

@RequestBody 方法参数可以使用 @Valid 进行注释,以调用类似于对 @ModelAttribute 方法参数的支持的自动验证。生成的MethodArgumentNotValidExceptionDefaultHandlerExceptionResolver 中处理并生成400 响应代码。

换句话说,如果您使用@Valid @RequestBody,那么Spring 将在调用您的方法之前拒绝无效请求。如果你的方法调用,那么你可以假设请求体是有效的。

BindingResult 用于验证表单/命令对象,而不是@RequestBody

【讨论】:

我期待着尝试一下!谢谢! 我们尝试删除 BindingResult,但验证无效。 我们正在使用 JSR303,我在模型类中遇到了空值问题。我想将错误发送回视图,但在 DefaultHandler 中处理它们会触发 MethodArgumentNotValidException。我认为到那时我无法优雅地处理这件事为时已晚。 这里是MethodArgumentNotValidException处理的样本:dzone.com/articles/spring-31-valid-requestbody【参考方案2】:

我不得不做一次类似的事情。我刚刚创建了一个可以将 JSON 转换为的 Java 对象并使用GSON 进行转换,从而使我的生活变得更简单。

其实很简单:

@Autowired
private Gson gson;

@RequestMapping(value = "/path/info", method = RequestMethod.POST)
public String myMethod(@RequestParam(value = "data") String data,
                       Model model,
                       @Valid MyCustomObject myObj,
                       BindingResult result) 
    //myObj does not contain any validation information.
    //we are just using it as as bean to take advantage of the spring mvc framework.
    //data contains the json string.
    myObj = gson.fromJson(data, MyCustomObject.class);

    //validate the object any way you want. 
    //Simplest approach would be to create your own custom validator 
    //to do this in Spring or even simpler would be just to do it manually here.
    new MyCustomObjValidator().validate(myObj, result);

    if (result.hasErrors()) 
        return myErrorView;
    
    return mySuccessView;

在您的自定义 Validator 类中进行所有验证:

public class MyCustomObjValidator implements Validator 

    @Override
    public boolean supports(Class<?> clazz) 
        return MyCustomObj.class.equals(clazz);
    

    @Override
    public void validate(Object target, Errors errors) 
        MyCustomObj c = (MyCustomObj) target;
        Date startDate = c.getStartDate();
        Date endDate = c.getEndDate();
        if (startDate == null) 
            errors.rejectValue("startDate", "validation.required");
        
        if (endDate == null) 
            errors.rejectValue("endDate", "validation.required");
        
        if(startDate != null && endDate != null && endDate.before(startDate))
            errors.rejectValue("endDate", "validation.notbefore.startdate");
        
    


MyCustomObject 不包含任何用于验证的注释,这是因为否则 Spring 将尝试验证此对象中当前为空的字段,因为所有数据都在 JSON 字符串中,例如:

public class MyCustomObject implements Serializable 
    private Date startDate;
    private Date endDate;

    public Date getStartDate() 
        return startDate;
    

    public Date getEndDate() 
        return endDate;
    

    public void setStartDate(Date theDate) 
        this.startDate = theDate;
    

    public void setEndDate(Date theDate) 
        this.endDate = theDate;
    

【讨论】:

您的帖子中的@RequestParam(value = "data") 是什么?我试图做这样的想法,但我有一个例外,它是必需的属性。 解决方案显式调用Validator,这应该由Spring框架完成。【参考方案3】:

尝试使用以下方法:

@Autowired
private FooValidator fooValidator;

@InitBinder("specificRequest") // possible to leave off for global behavior
protected void initBinder(WebDataBinder binder)
    binder.setValidator(fooValidator);

@ModelAttribute("specificRequest")
public Map<String, String> getModel() 
    return new HashMap<String, String>();

这将使您的控制器将请求序列化为您指定的类型。 我不得不说我通常不会为验证器提供服务(自动装配),但它可能会更好。

您的处理程序现在看起来像这样:

@RequestMapping(value="/somepath/foo", method=RequestMethod.POST)
public @ResponseBody Map<String, String> fooBar(
    @Valid @ModelAttribute("specificRequest") 
    Map<String, String> specificRequest, BindingResult results) 

    out("fooBar called");

    // get vin from JSON (reportRequest)

    return null;

据我所知,这可以完美地解决您收到的错误。

【讨论】:

以上是关于使用@Valid+BindingResult进行controller参数校验的主要内容,如果未能解决你的问题,请参考以下文章

为啥 BindingResult 必须遵循 @Valid?

SpringMVC数据校验Bug-@Valid不起效BindingResult读取不到Error信息

使用aop和BindingResult进行参数验证

带有 BindingResult 的 @Valid JSON 请求导致 IllegalStateException

在 Spring Boot 应用程序中使用 @Valid 和 BindingResult 时的表单输入验证问题

接口参数校验之@Valid与BindingResult