我如何确保解析期间的异常导致与验证失败返回的(自定义)响应相同类型的响应?
How can I make sure exceptions during parsing lead to the same kind of response as the (custom) response returned for validation failures?
我正在使用 Spring 创建一个 API,但我在引入自定义错误报告(部分)请求正文验证时遇到了一些问题。
当出现 parsing/validation 错误时,我想给用户一个自定义响应。
通过使用带有 @ControllerAdvice
.
注释的自定义 ResponseEntityExceptionHandler
,这适用于带有 @Valid
注释的字段以及 @javax.validation.constraints.NotNull
等验证器
但是,如果在解析请求正文时抛出异常(甚至在验证 运行 之前),它就不起作用。在那种情况下,我得到一个 html 状态为 500 (Server Error)
的错误页面
如何确保解析期间的异常导致与我 return 验证失败时的(自定义)响应相同类型的响应?
我的端点代码如下所示:
@RequestMapping(value= "/endpoint"
produces = { "application/json" },
consumes = { "application/json" },
method = RequestMethod.POST)
default ResponseEntity<Object> postSomething(@Valid @RequestBody MyRequestBody requestData){
// ...
}
MyRequestBody class 看起来像这样:
@Validated
public class MyRequestData {
@JsonProperty("stringValue")
private String stringValue = null;
@NotNull
@Valid
public String getStringValue() {
return stringValue;
}
// ...
public enum EnumValueEnum {
VALUE_1("value 1"),
VALUE_1("value 2");
private String value;
EnumValueEnum(String value) {
this.value = value;
}
@Override
@JsonValue
public String toString() {
return String.valueOf(value);
}
@JsonCreator
public static EnumValueEnum fromValue(String text) {
if(text == null){
return null;
}
for (EnumValueEnum b : EnumValueEnum.values()){
if (String.valueOf(b.value).equals(text)) {
return b;
}
}
throw new HttpMessageNotReadableException("EnumValueEnum \"" + text + "\" does not exist");
}
}
@JsonProperty("enumValue")
private EnumValueEnum enumValue = null;
}
自定义验证错误处理(和报告)如下所示:
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class MyValidationHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// return status(BAD_REQUEST).body(new ValidationResponse(ex.getBindingResult().getFieldErrors()));
}
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// return status(BAD_REQUEST).body(new ValidationResponse((JsonMappingException) ex.getCause()));
}
}
在此代码中,如果用户发送带有不存在的枚举值的请求,则会抛出 HttpMessageNotReadableException。我想在某个地方捕获它,并将其替换为与我所做的其他异常处理一致的自定义响应。 Where/How我可以这样做吗?
我找到了解决我自己问题的方法。
其实你可以使用Spring MVC的正常异常处理:
用 @ExceptionHandler
注释一个方法将使 Spring 尝试使用它来处理指定的异常类型(在注释的值字段或方法的参数中)。此方法可以放在控制器中,甚至可以放在我用于其他验证响应处理的 ResponseEntityExceptionHandler
中。
@ExceptionHandler
public ResponseEntity handle(HttpMessageConversionException e){
// return status(BAD_REQUEST).body(new ValidationResponse((JsonMappingException) e.getCause()));
}
注意你处理的异常类型:
这里的问题是解析时抛出的异常被包装在 JsonMappingException
的(某些子类型)中,而后者又被包装在 HttpMessageConversionException
.
中
e instanceof HttpMessageConversionException
e.getCause() instanceof JsonMappingException
e.getCause().getCause() // == your original exception
因此 @ExceptionHandler
应该接受 HttpMessageConversionException
而不是最初抛出的异常 (在我的例子中是 HttpMessageNotReadableException
)
如果你写一个只接受你原来的异常的 @ExceptionHandler
将不起作用!
我正在使用 Spring 创建一个 API,但我在引入自定义错误报告(部分)请求正文验证时遇到了一些问题。
当出现 parsing/validation 错误时,我想给用户一个自定义响应。
通过使用带有 @ControllerAdvice
.
注释的自定义 ResponseEntityExceptionHandler
,这适用于带有 @Valid
注释的字段以及 @javax.validation.constraints.NotNull
等验证器
但是,如果在解析请求正文时抛出异常(甚至在验证 运行 之前),它就不起作用。在那种情况下,我得到一个 html 状态为 500 (Server Error)
如何确保解析期间的异常导致与我 return 验证失败时的(自定义)响应相同类型的响应?
我的端点代码如下所示:
@RequestMapping(value= "/endpoint"
produces = { "application/json" },
consumes = { "application/json" },
method = RequestMethod.POST)
default ResponseEntity<Object> postSomething(@Valid @RequestBody MyRequestBody requestData){
// ...
}
MyRequestBody class 看起来像这样:
@Validated
public class MyRequestData {
@JsonProperty("stringValue")
private String stringValue = null;
@NotNull
@Valid
public String getStringValue() {
return stringValue;
}
// ...
public enum EnumValueEnum {
VALUE_1("value 1"),
VALUE_1("value 2");
private String value;
EnumValueEnum(String value) {
this.value = value;
}
@Override
@JsonValue
public String toString() {
return String.valueOf(value);
}
@JsonCreator
public static EnumValueEnum fromValue(String text) {
if(text == null){
return null;
}
for (EnumValueEnum b : EnumValueEnum.values()){
if (String.valueOf(b.value).equals(text)) {
return b;
}
}
throw new HttpMessageNotReadableException("EnumValueEnum \"" + text + "\" does not exist");
}
}
@JsonProperty("enumValue")
private EnumValueEnum enumValue = null;
}
自定义验证错误处理(和报告)如下所示:
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class MyValidationHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// return status(BAD_REQUEST).body(new ValidationResponse(ex.getBindingResult().getFieldErrors()));
}
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// return status(BAD_REQUEST).body(new ValidationResponse((JsonMappingException) ex.getCause()));
}
}
在此代码中,如果用户发送带有不存在的枚举值的请求,则会抛出 HttpMessageNotReadableException。我想在某个地方捕获它,并将其替换为与我所做的其他异常处理一致的自定义响应。 Where/How我可以这样做吗?
我找到了解决我自己问题的方法。
其实你可以使用Spring MVC的正常异常处理:
用 @ExceptionHandler
注释一个方法将使 Spring 尝试使用它来处理指定的异常类型(在注释的值字段或方法的参数中)。此方法可以放在控制器中,甚至可以放在我用于其他验证响应处理的 ResponseEntityExceptionHandler
中。
@ExceptionHandler
public ResponseEntity handle(HttpMessageConversionException e){
// return status(BAD_REQUEST).body(new ValidationResponse((JsonMappingException) e.getCause()));
}
注意你处理的异常类型:
这里的问题是解析时抛出的异常被包装在 JsonMappingException
的(某些子类型)中,而后者又被包装在 HttpMessageConversionException
.
e instanceof HttpMessageConversionException
e.getCause() instanceof JsonMappingException
e.getCause().getCause() // == your original exception
因此 @ExceptionHandler
应该接受 HttpMessageConversionException
而不是最初抛出的异常 (在我的例子中是 HttpMessageNotReadableException
)
如果你写一个只接受你原来的异常的 @ExceptionHandler
将不起作用!