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

Spring MVC:在REST控制器中注册自定义验证器的正确方法是什么

晋承嗣
2023-03-14

我试图弄清楚验证在Spring是如何工作的。到目前为止,我了解到有两种方法可以验证用户输入数据

  • JSR-303基于javax.validation.constraints注释的验证。这种验证最适合于简单的对象字段验证。但是您也可以实现您的自定义类型级别注释,以便基于检查多个字段的值执行更复杂的验证。
  • Spring验证基于org.springframework.validation.Validator接口。似乎更适合更复杂的验证。

如果我想使用这两种方法,在controller中注册自定义验证器的正确方法是什么?

这就是我正在努力做的。

我的自定义验证器。

public class PasswordPairValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return PasswordPair.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        PasswordPair password = (PasswordPair) target;
        if (!password.getPassword().equals(password.getRepeatPassword())) {
            errors.reject("passwordField", "passwords don't match");
        }
    }
}

我的控制器。

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(new PasswordPairValidator());
    }

    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<UserInfo> createUser(
            @RequestBody @Valid UserInfo userInfo) {
        userInfo.setId(123);
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                .path("/{id}").buildAndExpand(userInfo.getId()).toUri();
        return ResponseEntity.created(location).body(userInfo);
    }

    @RequestMapping(value = "/change_password", method = RequestMethod.POST)
    public ResponseEntity<UserInfo> changePassword(
            @RequestBody @Valid PasswordPair password) {
        UserInfo user = new UserInfo("test@gmail.com", "testuser");
        user.setId(123);
        return ResponseEntity.ok().body(user);
    }
}

当我调用createUserendpoint时,代码会失败,并出现以下错误:

ERROR c.e.testapp.controller.GlobalExceptionHandler - Internal Server Error
java.lang.IllegalStateException: Invalid target for Validator [com.example.testapp.validation.PasswordPairValidator@49acd001]: com.example.testapp.domain.UserInfo@cae4750

问题显然是Spring试图将PasswordPairValidator应用于UserInfo对象,这不是我的本意。为什么Spring不使用validator的supports()方法来检查validator可以应用于哪些对象
在另一个stackoverflow问题中,我发现我需要为@InitBinder注释指定值以使其工作,并且该值应该是“passwordPair”。但这个值是什么,因为它不是类名(“PasswordPair”)或方法参数值(“password”)?

第二个问题是,如果我想添加几个验证器,我需要定义多个@InitBinder(“value”)方法,还是有一种更简单的方法来实现?

最后一个问题,也许最好对所有事情使用基于注释的验证,验证单独的字段,并使用ConstraintValidator实现类型级别的自定义注释,以执行更复杂的验证?这些方法的利弊有点令人困惑。

共有2个答案

桂阳文
2023-03-14

必须为@InitBinder注释提供参数。请参考这个问题

上述问题还回答了您关于注册多个验证器的其他问题。

艾奕
2023-03-14

我认为发生这种情况的原因是,每次处理请求时都会调用@InitBinder方法,因此对于所有与HTTP谓词对应的方法。

我知道您可以限制用@InitBinder注释的方法被调用的次数的唯一方法是使用注释所需要的值参数。我承认,我也有点搞不懂那个价值是什么,或者它是如何被解释的。

Spring boot使用支持来检查是否每次调用initBinder()时都可以使用验证器,但在不合适时会抛出异常。当处理请求时调用initBinder()时,就会发生这种情况。因此,即使您有多个验证器,其中一个对请求体有效,它也会失败

如果有人能帮助我们如何在Spring boot中正确应用验证器,我也将不胜感激。在C#中,我知道您可以在中间件中注册bean,并根据您注册验证器的类,调用正确的验证器。(我对C#不太精通,但这就是我所记得的)。这样的事情在Java中不也是可能的吗?

 类似资料:
  • 我正在开发一个使用Log4j(1.2.16)日志记录的Web应用程序。为了避免编辑大量文件,我试图钩住自定义LoggerFactory实现,防止日志伪造。 似乎可以设置配置设置(project使用属性文件方法)指定要使用的记录器工厂。然而,这似乎不起作用。在检查了Log4j的源代码之后,似乎从未真正使用过该属性,即使它是由PropertyConfigurator类读取的。 检查更多的Log4j源代

  • 我想验证请求参数。我已经浏览了很多博客并回答了问题,我也做了同样的事情。在控制器类上添加了@Validated。 控制器中的方法: 控制器建议 配置: 完成所有这些之后,现在我得到一个404错误。"状态":404,"错误":"未找到","消息":"没有可用的消息"

  • 我有一个spring rest服务,它接受Person对象。Person对象具有名称、电话号码和电子邮件。当添加一个人时,电话号码是强制性的。如果存在电话号码属性,它将使用或或验证是否为空。但是如果属性不存在,验证就不起作用。请建议提供任何验证注释,以检查JSON请求中是否存在该属性。 以下是请求体测试用例 {“name”:“anu”,“phonenumber”:“”,“email”:“test@

  • 那里是自定义验证器: 当我调用时,我在插入的的上得到了一个。 我假设Spring会正确地注入定制验证器,我必须告诉他。但是,我不知道怎么做。我已经尝试了一些通过堆栈溢出和谷歌找到的东西,但没有运气。 任何帮助都将不胜感激。

  • null 现在的问题是: > 作为控制器方法执行的结果,我将返回对象的映射,该映射将解析为JSON。可能的实体是: {“结果”:“12”} 我应该如何提供基于结果的附加消息?我能想到两个解决办法: > 返回映射,该映射在肯定验证结果的情况下具有result=true,在否定验证结果的情况下具有result=false,以及带有相应消息的errorCode 返回指示验证结果的布尔值,并另外对否定结果

  • 我在项目中使用bean验证,我想为现有的约束注释编写一个自定义验证器。 例如,我有一个类,它表示一个名为CustomDateTime的日期/时间。在使用此类作为例如出生日期的类中,我想用过去的日期对字段进行注释: 然后,我通过实现ConstraintValidator创建一个自定义验证器 我知道您通常会创建这样的单独注释: 但对我来说,这似乎是双重代码;-) 如何注册要与一起使用的自定义验证器?