首页 > 解决方案 > 处理SpringBoot中绑定到@RequestParam的对象抛出的异常

问题描述

在这篇文章之后: http: //dolszewski.com/spring/how-to-bind-requestparam-to-object/

我创建了以下控制器:

    @GetMapping("/highlights")
    public ResponseEntity<List<Highlight>> getHighlights(
            HighlightFilterCriteria highlightFilterCriteria
            ) {
        //...

其中 HighlightFilterCriteria POJO 具有以下形状:

public class HighlightFilterCriteria {

    private LocalDateTime updatedDate;

    private UUID userId;

    public LocalDateTime getUpdatedDate() {
        return updatedDate;
    }

    public void setUpdatedDate(String updatedDate) {
        DateTimeFormatter dateTimeFormat = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
        try {
            this.updatedDate = LocalDateTime.parse(updatedDate, dateTimeFormat);
        } catch (DateTimeParseException exc) {
            throw new InvalidInputException("udpatedDate", "should be ISO date");
        }
    }

    public UUID getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        try {
            this.userId = UUID.fromString(userId);
        } catch (IllegalArgumentException exc) {
            throw new InvalidInputException("userId", "should be UUID");
        }

    }
}

这是我创建的自定义异常的实现InvalidInputException

package com.hmhco.rcehighlightspoc.exceptionsHandling;

public class InvalidInputException extends RuntimeException {

    private String paramName;
    private String message;

    public InvalidInputException(String paramName, String message) {
        super("\"" + paramName + "\" provided is invalid. It should be " + message);
        this.paramName = paramName;
        this.message = message;
    }
}

然后是带有异常处理程序InvalidInputException和通用捕获所有Exception处理程序的 ControllerAdvice 实现:

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = InvalidInputException.class)
    public ResponseEntity<Object> handleInvalidInputException(InvalidInputException ex) {
        log.info("Handled: Invalid input", ex);
        return buildResponseEntity(new ApiErrorResponse(
                HttpStatus.BAD_REQUEST,
                "Incorrect input",
                ex
        ));
    }

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<Object> handleException(Exception ex) {
        log.error("Handled: Catch all exception", ex);
        return buildResponseEntity(new ApiErrorResponse(
                HttpStatus.BAD_REQUEST,
                "Catch all exception handler",
                ex
        ));
    }

    private ResponseEntity<Object> buildResponseEntity(ApiErrorResponse errorResponse) {
        return new ResponseEntity<>(errorResponse, errorResponse.getStatus());
    }

当我使用格式错误的 userId query param (incorrect UUID) 发送请求时http://localhost:8080/v1/highlights?updatedDate=2020-05-18T08:58:33.876Z&userId=badUUID,try/catch 中的HighlightFilterCriteria捕获InvalidInputException并将其作为我的自定义重新抛出,InvalidInputException但不幸的是,ControllerAdvice 的handleInvalidInputException处理程序InvalidInputException由于某种原因没有捕获它。

它被@ExceptionHandler(value = Exception.class)处理程序捕获,我可以在日志中看到:

ERROR com.hmhco.rcehighlightspoc.exceptionsHandling.GlobalExceptionHandler.handleException - Handled: Catch all exception
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'highlightFilterCriteria' on field 'userId': rejected value [abcacf9b-926a-49c4-b980]; codes [methodInvocation.highlightFilterCriteria.userId,methodInvocation.userId,methodInvocation.java.util.UUID,methodInvocation]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [highlightFilterCriteria.userId,userId]; arguments []; default message [userId]]; default message [Property 'userId' threw exception; nested exception is com.hmhco.rcehighlightspoc.exceptionsHandling.InvalidInputException: "userId" provided is invalid. It should be should be UUID]
    at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:164)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
    ...

我希望由于控制器使用此 POJO 来解析请求参数,因此可以捕获自定义异常。

如果作为测试,我InvalidInputException通过以下方式直接从控制器中抛出自定义:

    @GetMapping("/highlights")
    public ResponseEntity<List<Highlight>> getHighlights(
            @Valid HighlightFilterCriteria highlightFilterCriteria
            ) {
        if (true) throw new InvalidInputException("userId", "should be UUID");
        ...
    }

然后我可以看到InvalidInputException处理程序按预期捕获它。

INFO  com.hmhco.rcehighlightspoc.exceptionsHandling.GlobalExceptionHandler.handleInvalidInputException - Handled: Invalid input
com.hmhco.rcehighlightspoc.exceptionsHandling.InvalidInputException: "userId" provided is invalid. It should be should be UUID
    at com.hmhco.rcehighlightspoc.controller.HighlightController.getHighlights(HighlightController.java:63)

主要问题是为什么InvalidInputException没有被异常处理程序捕获但被处理程序捕获ControllerAdviceInvalidInputExceptionException

或者,也许我试图以不正确的方式在 POJO 中实现验证,如果是这样,当控制器方法获取 POJO 作为参数时,处理复杂类型(如 ISO 格式的 UUID 和 Date)的解析错误的正确方法是什么?

标签: spring-bootparsingcontrollerpojo

解决方案


推荐阅读