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

如何自定义javax.validation.Valid的枚举参数校验错误响应信息?

问题

现有如下Spring控制器代码:

@RequestMapping(
    method = RequestMethod.GET,
    value = Endpoints.TRUE_MATCH,
    produces = {"application/json"})
public ResponseEntity<ResponseWrapper<List<TrueMatch>>> getTrueMatch(
    @Valid Details details) {
    ...
}

Details类包含字段:@NotNull private TransmissionType transmissionType;,其中TransmissionType是枚举类型。当请求中的transmissionType参数与枚举值不匹配时,返回的错误响应如下:

{
    "status": 400,
    "validationErrors": {
        "transmissionType": "Failed to convert property value of type 'java.lang.String' to required type 'my.application.model.TransmissionType' for property 'transmissionType'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@javax.validation.constraints.NotNull ie.aviva.services.motor.cartellservice.model.TransmissionType] for value 'foo'; nested exception is java.lang.IllegalArgumentException: No enum constant ie.aviva.services.motor.cartellservice.model.TransmissionType.automatic'
    },
    "title": "Bad Request"
}

需要将transmissionType字段的错误消息替换为自定义内容,使响应变为:

{
    "status": 400,
    "validationErrors": {
        "transmissionType": "Some custom message"
    },
    "title": "Bad Request"
}
解决方案

可以通过以下几种方式实现自定义错误消息:

1. 自定义枚举Converter并处理转换异常

创建自定义Converter处理字符串到枚举的转换,转换失败时抛出自定义异常,再通过全局异常处理器捕获并返回自定义消息。

步骤1:实现Converter

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class StringToTransmissionTypeConverter implements Converter<String, TransmissionType> {
    @Override
    public TransmissionType convert(String source) {
        try {
            // 根据枚举实际命名规则调整大小写处理逻辑
            return TransmissionType.valueOf(source.toUpperCase());
        } catch (IllegalArgumentException e) {
            throw new InvalidTransmissionTypeException("Some custom message");
        }
    }
}

自定义异常类:

public class InvalidTransmissionTypeException extends RuntimeException {
    public InvalidTransmissionTypeException(String message) {
        super(message);
    }
}

步骤2:全局异常处理器捕获异常

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(InvalidTransmissionTypeException.class)
    public ResponseEntity<Map<String, Object>> handleInvalidTransmissionType(InvalidTransmissionTypeException ex) {
        Map<String, Object> response = new HashMap<>();
        response.put("status", HttpStatus.BAD_REQUEST.value());
        response.put("title", "Bad Request");
        
        Map<String, String> validationErrors = new HashMap<>();
        validationErrors.put("transmissionType", ex.getMessage());
        response.put("validationErrors", validationErrors);
        
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }
}

2. 直接捕获Spring的转换异常

无需自定义Converter,直接通过全局异常处理器捕获ConversionFailedException,判断目标类型为TransmissionType时返回自定义消息:

import org.springframework.core.convert.ConversionFailedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ConversionFailedException.class)
    public ResponseEntity<Map<String, Object>> handleConversionFailed(ConversionFailedException ex) {
        if (ex.getTargetType().getType().equals(TransmissionType.class)) {
            Map<String, Object> response = new HashMap<>();
            response.put("status", HttpStatus.BAD_REQUEST.value());
            response.put("title", "Bad Request");
            
            Map<String, String> validationErrors = new HashMap<>();
            validationErrors.put("transmissionType", "Some custom message");
            response.put("validationErrors", validationErrors);
            
            return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
        }
        // 其他类型转换异常的默认处理
        return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }
}

3. 自定义校验注解(基于JSR-380)

通过自定义校验注解,直接校验输入的字符串是否为有效的枚举值,适合将字段定义为String类型的场景:

步骤1:自定义校验注解

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = ValidTransmissionTypeValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidTransmissionType {
    String message() default "Some custom message";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

步骤2:实现校验器

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;

public class ValidTransmissionTypeValidator implements ConstraintValidator<ValidTransmissionType, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return false; // 配合@NotNull使用,可根据需求调整
        }
        return Arrays.stream(TransmissionType.values())
                .anyMatch(enumVal -> enumVal.name().equalsIgnoreCase(value));
    }
}

步骤3:修改Details类

将字段改为String类型,后续可手动转换为枚举:

public class Details {
    @NotNull
    @ValidTransmissionType(message = "Some custom message")
    private String transmissionType;
    
    // 手动转换为枚举的方法
    public TransmissionType getTransmissionTypeEnum() {
        return TransmissionType.valueOf(transmissionType.toUpperCase());
    }
}

内容的提问来源于stack exchange,提问作者runnerpaul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 07:00:59