@RequestBody @Valid SomeDTO具有枚举类型字段,自定义错误消息

时间:2017-08-30 19:05:02

标签: java spring rest validation

我有以下@RestController

@RequestMapping(...)
public ResponseEntity(@RequestBody @Valid SomeDTO, BindingResult errors) {
//do something with errors if validation error occur
}

public class SomeDTO {
   public SomeEnum someEnum;
}

如果JSON请求是{ "someEnum": "valid value" },一切正常。但是,如果请求为{ "someEnum": "invalid value" },则仅返回错误代码400。

如何捕获此错误,以便我可以提供自定义错误消息,例如“someEnum必须具有值A / B / C”。

5 个答案:

答案 0 :(得分:2)

@Amit 提供的答案很好并且有效。如果您想以特定方式反序列化枚举,则可以继续进行。但该解决方案不可扩展。每个需要验证的枚举都必须用 @JsonCreator 注释。

其他答案无助于美化错误消息。

所以这是我对 spring 网络环境中所有枚举通用的解决方案。

@RestControllerAdvice
public class ControllerErrorHandler extends ResponseEntityExceptionHandler {
    public static final String BAD_REQUEST = "BAD_REQUEST";
    @Override
    public ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException exception,
                                                               HttpHeaders headers, HttpStatus status, WebRequest request) {
        String genericMessage = "Unacceptable JSON " + exception.getMessage();
        String errorDetails = genericMessage;

        if (exception.getCause() instanceof InvalidFormatException) {
            InvalidFormatException ifx = (InvalidFormatException) exception.getCause();
            if (ifx.getTargetType().isEnum()) {
                errorDetails = String.format("Invalid enum value: '%s' for the field: '%s'. The value must be one of: %s.",
                        ifx.getValue(), ifx.getPath().get(ifx.getPath().size()-1).getFieldName(), Arrays.toString(ifx.getTargetType().getEnumConstants()));
            }
        }
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setTitle(BAD_REQUEST);
        errorResponse.setDetail(errorDetails);
        return handleExceptionInternal(exception, errorResponse, headers, HttpStatus.BAD_REQUEST, request);
    }

}

这将处理所有类型的所有无效枚举值,并为最终用户提供更好的错误消息。

示例输出:

{
    "title": "BAD_REQUEST",
    "detail": "Invalid enum value: 'INTERNET_BANKING' for the field: 'paymentType'. The value must be one of: [DEBIT, CREDIT]."
}

答案 1 :(得分:1)

Yon可以使用@ControllerAdvice实现此目的,如下所示

@org.springframework.web.bind.annotation.ExceptionHandler(value = {InvalidFormatException.class})
    public ResponseEntity handleIllegalArgumentException(InvalidFormatException exception) {

        return ResponseEntity.badRequest().body(exception.getMessage());
    }

基本上,我们的想法是抓住com.fasterxml.jackson.databind.exc.InvalidFormatException并根据您的要求处理它。

答案 2 :(得分:1)

    def setUp(self):
    self.driver = webdriver.Remote(command_executor='http://localhost:4444/wd/hub', 
                                   desired_capabilities=DesiredCapabilities.FIREFOX) #remote-server or localhost

I created a fully functional Spring boot Application with a Test on Bitbucket

答案 3 :(得分:0)

@Valid has to do with Hibernate bean validation. Currently enum type is not supported out of the box. I found this answer to be the closet, https://funofprograming.wordpress.com/2016/09/29/java-enum-validator/, the drawback however is that you have to make the enum field of type String instead.

答案 4 :(得分:0)

您不需要@Valid进行枚举验证,您可以使用以下代码获得所需的响应:

控制器代码,StackDTO中包含一个枚举PaymentType:

@RequestMapping(value = "/reviews", method = RequestMethod.POST)
    @ResponseBody
    public ResponseEntity<String> add(@RequestBody StackDTO review) {

        return new ResponseEntity<String>(HttpStatus.ACCEPTED);
    }

创建一个异常类,如EnumValidationException

public class EnumValidationException extends Exception {

    private String enumValue = null;
    private String enumName = null;

    public String getEnumValue() {
        return enumValue;
    }

    public void setEnumValue(String enumValue) {
        this.enumValue = enumValue;
    }

    public String getEnumName() {
        return enumName;
    }

    public void setEnumName(String enumName) {
        this.enumName = enumName;
    }

    public EnumValidationException(String enumValue, String enumName) {
        super(enumValue);

        this.enumValue = enumValue;
        this.enumName = enumName;
    }

    public EnumValidationException(String enumValue, String enumName, Throwable cause) {
        super(enumValue, cause);

        this.enumValue = enumValue;
        this.enumName = enumName;
    }
}

我有如下的枚举,在方法创建

上有一个特殊的注释@JsonCreator
public enum PaymentType {

    CREDIT("Credit"), DEBIT("Debit"); 

    private final String type;

    PaymentType(String type) {
        this.type = type;
    }

    String getType() {
        return type;
    }

    @Override
    public String toString() {
        return type;
    }

    @JsonCreator
    public static PaymentType create (String value) throws EnumValidationException {
        if(value == null) {
            throw new EnumValidationException(value, "PaymentType");
        }
        for(PaymentType v : values()) {
            if(value.equals(v.getType())) {
                return v;
            }
        }
        throw new EnumValidationException(value, "PaymentType");
    }
}

最后是RestErrorHandler类,

@ControllerAdvice
public class RestErrorHandler {

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ResponseEntity<ValidationErrorDTO> processValidationIllegalError(HttpMessageNotReadableException ex,
            HandlerMethod handlerMethod, WebRequest webRequest) {

        EnumValidationException exception = (EnumValidationException) ex.getMostSpecificCause();

        ValidationErrorDTO errorDTO = new ValidationErrorDTO();
        errorDTO.setEnumName(exception.getEnumName());
        errorDTO.setEnumValue(exception.getEnumValue());
        errorDTO.setErrorMessage(exception.getEnumValue() + " is an invalid " + exception.getEnumName());
        return new ResponseEntity<ValidationErrorDTO>(errorDTO, HttpStatus.BAD_REQUEST);
    }

}

ValidationErrorDTO是带有enumValue,enumName和errorMessage的setter / getters的dto。现在,当您使用以下请求将POST调用发送到控制器端点/评论时

{"paymentType":"Credit2"}

然后代码将响应返回为400,响应体位于以下 -

{
    "enumValue": "Credit2",
    "enumName": "PaymentType",
    "errorMessage": "Credit2 is an invalid PaymentType"
}

如果它能解决您的问题,请告诉我。