如何处理 Spring Boot Rest 和 Jackson Deserializer 的 Javax 验证中的无效数字

Posted

技术标签:

【中文标题】如何处理 Spring Boot Rest 和 Jackson Deserializer 的 Javax 验证中的无效数字【英文标题】:How to handle invalid Number in Javax validation for Spring Boot Rest and Jackson Deserializer 【发布时间】:2020-04-25 20:45:28 【问题描述】:

我有一个 Java Pojo 类,在我的 Rest Controller 中应该是 @RequestBody。这个 Java Pojo 类有一个 Integer 字段。

当用户调用 Rest API 并将 Integer 字段值作为垃圾字符串传递时,Jackson 将抛出 InvalidFormatException。相反,我想使用 Javax Validator 框架注释来处理此错误并显示有意义的错误消息作为响应。不幸的是,Jackson 从 JSON 到 Java 的反序列化发生在 Javax 验证之前,因此我的 @Digits 验证永远不会被调用。

注意,@NotNull 被调用,但 @Digits 未被调用,因为 jackson 在调用到达 Javax 验证层 IMO 之前很久就失败了请求。

Java Pojo 类:

public class Board implements Serializable
  private static final long serialVersionUID = 1L;


  @Digits(message = "quantity must be a number", integer = 8, fraction = 0)
  private Integer quantity;

  public Integer getQuantity() 
      return quantity;
  

  public void setQuantity(Integer quantity) 
      this.quantity= quantity;
  

控制器类:

@Controller
@RequestMapping("boards")
public class EnrichController 

    @Autowired
    private BoardService boardService;

    @RequestMapping(value = "", method = RequestMethod.PUT, produces = "application/json;charset=UTF-8")
    public @ResponseStatus(value = HttpStatus.NO_CONTENT) @ResponseBody void updateBoard(
            @RequestBody @Valid Board board) throws IllegalArgumentException, MalformedURLException 
        boardService.updateUserBoard(board);
    

用户输入:


  "quantity": "abcdef"

如您所见,我正在使用 Javax 验证注释 @Digits@Valid 但没有用,因为 Jackson 反序列化在解析整数字段 quantity 时失败。

您是否可以通过使用 Javax 验证注释处理此用例来帮助我解决这种情况?我认为在 POJO 类中将字段类型更改为 String 是一项昂贵的工作,因为每次我需要该字段上的一些业务逻辑时,我们都必须进行 String 到 Integer 的转换,因此这不是我的选择。

【问题讨论】:

Board 参数旁边添加一个BindingResult,以捕获错误并在控制器内部进行处理。或者写一个@ExceptionHandler 来提供正确的错误信息。 感谢@M.Deinum,我已经在ControllerAdvice 类中尝试过@Exceptionhandler,但它没有满足我在Javax 验证层中处理这个特定用例的要求。在这种情况下,错误总是由 Jackson 解析器处理。 它总是如此,除非您将类型更改为 String 并验证它是否为数字。没有办法将非整数分配给整数进行验证。您当前的设置根本无法实现您想要的。 所以基本上这个值被分配给pojo,然后才运行验证?这太丑陋了。我原以为验证会在尝试分配值之前运行,然后这种情况会完美解决。愚蠢的弹簧验证:(实际上,如果你发送一个字符串,至少它不应该分配一个数字,你可以验证 NotNull,对吧? 【参考方案1】:

好吧,在您的情况下,问题是您不符合公开的 REST API 并尝试为数量字段发送字符串而不是数字。这绝不应该由您或使用您的 API 的第三方服务发生。

您是否可以通过使用 Javax 验证注释处理此用例来帮助我解决这种情况?

无论如何,如果您仍想解决上述问题,解决方案是更改字符串类型字段中的“数量”并为其添加模式匹配

@Pattern(message = "quantity must be a number", regexp="^[0-9]*$")
private String quantity;

【讨论】:

谢谢,但是将 Pojo 字段从 Integer 转换为 String 非常昂贵。我负担不起,因为每次需要业务逻辑代码时都需要我手动进行转换。我正在寻找更多以了解是否存在框架级别的支持来绕过该特定字段上的这个 Jackson 解析错误,而是在 Javax 验证层中处理它。 不客气。我同意你的看法,这就是为什么我认为你不应该太关心绕过杰克逊,因为如果有人以这种方式调用你的 API,那么他们做错了,并且不尊重你的 API 结构。但是,如果您正在与系统集成,并且您确定他们正在以这种方式发送数据并且不会很快更改,那么唯一的解决方案是将其更改为字符串。之后您可以使用map struct 来简化转换过程。

以上是关于如何处理 Spring Boot Rest 和 Jackson Deserializer 的 Javax 验证中的无效数字的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 如何处理 Hibernate 会话?

Spring Data REST CORS - 如何处理预检选项请求?

如何处理 Spring Boot 重定向到 /error?

如何处理 Spring Rest Web 服务中的 JSON 解析错误

如何处理 grails spring-security-rest 插件中的自定义身份验证异常?

在 Spring Boot 中验证 JWT 时如何处理 InvalidSignatureException?