如何自定义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
相关产品推荐
相关产品推荐

