【问题标题】:How to return the ConstraintViolationException messages in the response body?如何在响应正文中返回 ConstraintViolationException 消息?
【发布时间】:2019-10-15 17:36:54
【问题描述】:

我正在开发一个基于 Spring Boot 的 REST API。我正在使用自定义 ConstraintValidator 注释验证输入实体。我的问题是我无法在响应中返回 ConstraintViolationException 消息。我的异常处理程序没有捕获异常(可能是因为它们被包装在其他类型的异常中)。

我可以就如何处理这种情况获得一些建议吗?

我在整个互联网上进行了搜索,但找不到适合我的解决方案,而且我还浪费了几个小时。

示例注释:

@Documented
@Retention(RUNTIME)
@Target({FIELD, PARAMETER})
@Constraint(validatedBy = BirthDateValidator.class)
public @interface ValidBirthDate {

    String message() default "The birth date is not valid.";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

验证器类:

public class BirthDateValidator extends FieldValidationBase implements ConstraintValidator<ValidBirthDate, LocalDate> {

    private static final Logger LOGGER = LoggerFactory.getLogger(BirthDateValidator.class);

    @Override
    public void initialize(ValidBirthDate constraintAnnotation) {
    }

    @Override
    public boolean isValid(LocalDate birthDate, ConstraintValidatorContext constraintValidatorContext) {
        constraintValidatorContext.disableDefaultConstraintViolation();
        LOGGER.info("Starting the validation process for birth date {}.", birthDate);

        if(birthDate == null) {
            constraintValidatorContext.buildConstraintViolationWithTemplate("The birth date is null.")
                    .addConstraintViolation();
            return false;
        }

        //other validations

        return true;
    }

模型类:

public class Manager extends BaseUser {

    //other fields 

    @Valid
    @ValidBirthDate
    private LocalDate birthDate;

    //setters & getters

异常处理程序:

@ExceptionHandler(value = ConstraintViolationException.class)
    public ResponseEntity handleConstraintViolationException(ConstraintViolationException ex, WebRequest request) {
        List<String> errors = new ArrayList<>();

        for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
            errors.add(violation.getRootBeanClass().getName() + ": " + violation.getMessage());
        }

        Error response = new Error(errors);
        return new ResponseEntity<Object>(response, new HttpHeaders(), BAD_REQUEST);
    }

控制器:

@RestController
@RequestMapping(value = "/register", consumes = "application/json", produces = "application/json")
public class RegistrationController {

    @Autowired
    private RegistrationService registrationService;

    @PostMapping(value = "/manager")
    public ResponseEntity registerManager(@RequestBody @Valid Manager manager) {
        registrationService.executeSelfUserRegistration(manager);
        return new ResponseEntity<>(new Message("User " + manager.getEmailAddress() + " registered successfully!"), CREATED);
    }
}

我收到 400 响应代码,但我没有看到任何包含违反约束消息的响应正文。

【问题讨论】:

  • 我也有一个通用的异常处理程序(用于Exception),但这也没有捕捉到它。我假设异常是由 Spring 在内部处理并包装到其他东西中,但我不知道那是什么。当对象未通过验证(isValid() 返回 false)时,不应该抛出 ConstraintViolationException 吗?
  • 您检查过birthDate 是否为空吗?
  • 是的(见上文)。我的日志中或调试时没有发现异常。
  • 在另一个线程上,有人提醒我们RestController类也需要“@Validated”注解。

标签: java spring spring-boot annotations bean-validation


【解决方案1】:

经过更多调试后,我发现所有违反约束的行为都被包装到 MethodArgumentNotValidException 中(因为 @Valid 注释)——我不得不深入研究该异常以获取我的信息。

我已经从 ResponseEntityExceptionHandler 覆盖了 handleMethodArgumentNotValid() 方法,这就是我让它工作的方式:

@Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

        List<String> errorMessages = new ArrayList<>();
        BindingResult bindingResult = ex.getBindingResult();
        List<ObjectError> errors = bindingResult.getAllErrors();
        for(ObjectError error : errors) {
            String message = error.getDefaultMessage();
            errorMessages.add(message);
        }

        return new ResponseEntity<>(new Error(errorMessages), new HttpHeaders(), BAD_REQUEST);
    }

也许这对某人有帮助。

【讨论】:

  • 这真的很有用,我一直在努力提取确切的约束违规,并且 MethodArgumentNotValidException 没有为我提供所需的结果,因为它是通用的。在我提供这样的代码后,我能够深入研究约束错误。非常感谢!
【解决方案2】:

当目标参数没有通过验证时,Spring Boot 会抛出一个MethodArgumentNotValidException 异常。我已经从bindingResult 中提取了这个异常的错误信息,如下所示:

@RestControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

@Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex, HttpHeaders headers,
            HttpStatus status, WebRequest request) {
        //to extract the default error message from a diagnostic
        // information about the errors held in MethodArgumentNotValidException
        Exception exception = new Exception(ex.getBindingResult().getAllErrors().get(0).getDefaultMessage());
        return this.createResponseEntity(HttpStatus.BAD_REQUEST, exception, request);
    }

private ResponseEntity<Object> createResponseEntity(
            HttpStatus httpStatus, Exception ex, WebRequest request) {
        ErrorResponse errorResponse = ErrorResponse.builder()
                .timestamp(LocalDateTime.now())
                .status(httpStatus.value())
                .error(httpStatus.getReasonPhrase())
                .message(ex.getMessage())
                .path(request.getDescription(true))
                .build();
        return handleExceptionInternal(ex, errorResponse,
                new HttpHeaders(), httpStatus, request);
    }

}

ErrorResponse 类:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ErrorResponse {

    private LocalDateTime timestamp;
    private int status;
    private String error;
    private String message;
    private String path;
}

响应为 400,正文为 JSON 格式,如下所示:

{
  "timestamp": "2021-01-20T10:30:15.011468",
  "status": 400,
  "error": "Bad Request",
  "message": "Due date should not be greater than or equal to Repeat Until Date.",
  "path": "uri=/api/tasks;client=172.18.0.5;user=109634489423695603526"
}

我希望这会有所帮助。如果您需要关于类级别约束的详细说明,请查看this video

【讨论】:

    猜你喜欢
    • 2022-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-09
    • 2018-02-22
    • 2019-12-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多