当前位置: 首页 > 知识库问答 >
问题:

在Controller方法中绑定到集合的Request estbody参数的Spring验证

庞彬
2023-03-14

我有

一个实体:

package org.ibp.soq;

public class MyEntity {

    private String field1;
    private String field2;

    //..getters and setters

}

实体的验证器:

package org.ibp.soq;

import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

@Component
public class MyEntityValidator implements Validator {

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

    @Override
    public void validate(Object target, Errors errors) {
        MyEntity myEntity = (MyEntity) target;
        // Logic to validate my entity
        System.out.print(myEntity);
    }

}

采用批量PUT方法的REST控制器:

package org.ibp.soq;

import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/myEntity")
public class MyEntityRestResource {

    @Autowired
    private MyEntityValidator myEntityValidator;

    @InitBinder
    protected void initBinder(final WebDataBinder binder) {
        binder.addValidators(this.myEntityValidator);
    }

    @RequestMapping(method = RequestMethod.PUT)
    public void bulkCreate(@RequestBody @Valid List<MyEntity> myEntities) {
        // Logic to bulk create entities here.
        System.out.print(myEntities);
    }
}

当我使用以下请求正文对此资源发出PUT请求时:

[
  {
    "field1": "AA",
    "field2": "11"
  },

  {
    "field1": "BB",
    "field2": "22"
  }
]

我得到的错误是:

"Invalid target for Validator [org.ibp.soq.MyEntityValidator@4eab617e]: [org.ibp.soq.MyEntity@21cebf1c, org.ibp.soq.MyEntity@c64d89b]"

我可以理解,这是因为MyEntityValidator“支持”单个MyEntity验证,而不是对ArrayList的验证

如果在请求正文中有一个MyEntity对象和一个带有有效MyEntity MyEntity参数的相应控制器方法,则MyEntityValidator可以完美地工作。

如何扩展我使用的验证器设置,以支持MyEntity集合的验证?

共有3个答案

苏浩瀚
2023-03-14

实际上,这可以通过使用Spring验证和JSR303来实现。

  • 公开MethodValidationPostProcessor bean
  • 用@Validated(org.springframework.validation.annotation.Validated)注释控制器类
  • 在MyEntity字段/方法上使用JSR303验证注释
  • 用@Valid注释RequestBody参数(您已经在示例中这样做了)
  • 添加@ExceptionHandler方法来处理MethodArgumentNotValidException。这可以在controller或@ControllerAdvice类中完成
笪德华
2023-03-14

正如您可能已经猜到的,使用Spring验证无法实现这一点。Spring验证实现Bean验证(JSR303/349),而不是对象验证。不幸的是,集合不是Java Bean。你有两个选择

  • 把你的清单包在Java里
  • 在您的批量创建方法myEntityValidator中手动调用验证器。验证(目标对象,错误)
宇文鸿振
2023-03-14

解决方案是为集合创建一个自定义的验证器,以及一个@ControllerAdviceWebDataBinder中注册该验证器。

验证器:

import java.util.Collection;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

/**
 * Spring {@link Validator} that iterates over the elements of a 
 * {@link Collection} and run the validation process for each of them
 * individually.
 *   
 * @author DISID CORPORATION S.L. (www.disid.com)
 */
public class CollectionValidator implements Validator {

  private final Validator validator;

  public CollectionValidator(LocalValidatorFactoryBean validatorFactory) {
    this.validator = validatorFactory;
  }

  @Override
  public boolean supports(Class<?> clazz) {
    return Collection.class.isAssignableFrom(clazz);
  }

  /**
   * Validate each element inside the supplied {@link Collection}.
   * 
   * The supplied errors instance is used to report the validation errors.
   * 
   * @param target the collection that is to be validated
   * @param errors contextual state about the validation process
   */
  @Override
  @SuppressWarnings("rawtypes")
  public void validate(Object target, Errors errors) {
    Collection collection = (Collection) target;
    for (Object object : collection) {
      ValidationUtils.invokeValidator(validator, object, errors);
    }
  }
}

控制器建议:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;

/**
 * Controller advice that adds the {@link CollectionValidator} to the 
 * {@link WebDataBinder}.
 * 
 * @author DISID CORPORATION S.L. (www.disid.com)
 */
@ControllerAdvice
public class ValidatorAdvice {

  @Autowired
  protected LocalValidatorFactoryBean validator;


  /**
   * Adds the {@link CollectionValidator} to the supplied 
   * {@link WebDataBinder}
   * 
   * @param binder web data binder.
   */
  @InitBinder
  public void initBinder(WebDataBinder binder) {
    binder.addValidators(new CollectionValidator(validator));
  }
}

 类似资料:
  • 我为我的模型定制了一个 : 我希望当在请求中传递无效值时,HTTP400错误请求将自动返回。然而,这种情况并不发生。如果存在任何绑定错误,应该怎样做才能使Web API返回HTTP400?

  • 问题内容: 在Spring MVC中,将请求 参数 绑定到处理请求的方法参数很容易。我只是用。但是我可以对request 属性 做同样的事情吗?当前,当我想访问request 属性时 ,我必须执行以下操作: 但是我真的很想使用这样的东西: 不幸的是,这种方式行不通。我可以以某种方式扩展Spring功能并添加自己的“绑定器”吗? 编辑 (我要实现的目标) :我将当前登录的用户存储在request属性

  • 但是使用此代码会出现冲突,因为我已经用FXML代码定义了项目中的控制器,要解决这一问题,删除FXML代码中的段就足够了,但是我不会这么做,因为将代码留在FXML中允许我访问SceneBuilder的一些好特性。

  • 我有一个绑定到< code >列表的wpf组合框 如果我的数据源是自定义集合,那么绑定很容易,我应该只从自定义集合传递属性名称,但由于绑定源是字符串列表,绑定属性应该是什么?

  • 我最近在读Java中的思维(第四版)时,遇到了一个关于Java中方法绑定的问题。首先让我们看看书中的两个定义: 将方法调用连接到方法体称为绑定。 Java中的所有方法绑定都使用后期绑定,除非方法是静态的或最终的。 您可以在多态性章节的方法调用绑定一节中找到这些定义。(第281-282页) null

  • 相信 Python 程序员多多少少都和我一样遇到过 Method Unbound Error,直译过来就是 “方法未绑定错误”,虽然搜索之后知道了使用 @classmethod 这样的装饰起后就可以解决问题, 但是一直没有得到完全解惑。 我们知道,Python 是一个动态语言,在类的创建过程中甚至实例化以后都能动态地修改其方法、 属性,这种做法通常被称为“Monkey Patching”,虽然我们