You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何验证传入Float类型DTO的字符串输入并返回自定义错误

解决Spring中Float字段非法格式输入的自定义验证问题

问题根源

当传入"99A.99"这类无法反序列化为Float的字符串时,Jackson的反序列化流程优先于Spring Validation执行,此时直接抛出JSON反序列化异常,导致自定义的@ValidFloatFormat注解根本无法触发,最终返回系统默认的400错误,而非预期的自定义验证响应。

可行解决方案

方案一:用String接收字段,在验证阶段处理格式转换

让Jackson正常完成反序列化,把格式校验逻辑交给自定义注解:

  1. 修改BookDTO的price字段类型为String:
public class BookDTO {
    @NotNull
    @ValidFloatFormat(message="INVALID FLOAT")
    @DecimalMin(value = "0.00", inclusive = true, message = "Price should not be less than 0.00")
    @DecimalMax(value = "99.99", inclusive = true, message = "Price should not be more than 99.99")
    private String price;
    // getter、setter
}
  1. 调整自定义验证器,针对String类型做格式校验:
public class FloatFormatValidator implements ConstraintValidator<ValidFloatFormat, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true; // 空值逻辑交给@NotNull处理
        }
        try {
            Float.parseFloat(value);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }
}
  1. 业务层使用时将String转为Float即可:
Float price = Float.parseFloat(bookDTO.getPrice());

方案二:自定义Jackson反序列化器,统一异常处理

通过自定义反序列化器捕获格式错误,再通过全局异常处理器转换成统一响应格式:

  1. 自定义Float反序列化器:
public class CustomFloatDeserializer extends StdDeserializer<Float> {
    public CustomFloatDeserializer() {
        super(Float.class);
    }

    @Override
    public Float deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String input = p.getText().trim();
        try {
            return Float.parseFloat(input);
        } catch (NumberFormatException e) {
            // 抛出带自定义提示的格式异常
            throw new InvalidFormatException(p, "INVALID FLOAT", input, Float.class);
        }
    }
}
  1. 在BookDTO的price字段上绑定反序列化器:
public class BookDTO {
    @NotNull
    @DecimalMin(value = "0.00", inclusive = true, message = "Price should not be less than 0.00")
    @DecimalMax(value = "99.99", inclusive = true, message = "Price should not be more than 99.99")
    @JsonDeserialize(using = CustomFloatDeserializer.class)
    private Float price;
    // getter、setter
}
  1. 编写全局异常处理器,统一处理格式异常和验证异常:
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 处理反序列化格式错误
    @ExceptionHandler(InvalidFormatException.class)
    public ResponseEntity<ValidationErrorResponse> handleInvalidFormat(InvalidFormatException ex) {
        ValidationErrorResponse response = new ValidationErrorResponse();
        List<String> errors = new ArrayList<>();
        if ("INVALID FLOAT".equals(ex.getMessage())) {
            errors.add("INVALID FLOAT");
        } else {
            errors.add("字段格式错误:" + ex.getPath().get(0).getFieldName());
        }
        response.setErrors(errors);
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }

    // 处理Spring Validation的其他错误(如@DecimalMin等)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ValidationErrorResponse> handleValidationErrors(MethodArgumentNotValidException ex) {
        ValidationErrorResponse response = new ValidationErrorResponse();
        List<String> errors = ex.getBindingResult()
                .getAllErrors()
                .stream()
                .map(error -> error.getDefaultMessage())
                .collect(Collectors.toList());
        response.setErrors(errors);
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }
}
  1. 统一响应结构示例(ValidationErrorResponse):
public class ValidationErrorResponse {
    private int status;
    private List<String> errors;
    // getter、setter方法
}

内容的提问来源于stack exchange,提问作者Mr. Kenneth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 06:28:20