问题描述
我正在寻找一种处理将请求参数绑定到DTO字段期间引发的自定义异常的方法.
I am looking for a way to handle custom exception thrown during binding of request parameter to DTO field.
我在Spring Boot应用程序中有一个cantroller,如下所示
I have a cantroller in Spring Boot application as follows
@GetMapping("/some/url")
public OutputDTO filterEntities(InputDTO inputDTO) {
return service.getOutput(inputDTO);
}
输入DTO的字段很少,其中之一是枚举类型
input DTO has few fields, one of which is of enum type
public class InputDTO {
private EnumClass enumField;
private String otherField;
/**
* more fields
*/
}
用户将以这种方式访问URL
user will hit the URL in ths way
localhost:8081/some/url?enumField=wrongValue&otherField=anyValue
现在,如果用户为enumField发送了错误的值,我想将CustomException与特定消息一起抛出.活页夹中实现了枚举实例创建和引发异常的过程
Now if user sends wrong value for enumField, I would like to throw my CustomException with particular message. Process of enum instance creation and throwing of exception is implemented in binder
@InitBinder
public void initEnumClassBinder(final WebDataBinder webdataBinder) {
webdataBinder.registerCustomEditor(
EnumClass.class,
new PropertyEditorSupport() {
@Override
public void setAsText(final String text) throws IllegalArgumentException {
try {
setValue(EnumClass.valueOf(text.toUpperCase()));
} catch (Exception exception) {
throw new CustomException("Exception while deserializing EnumClass from " + text, exception);
}
}
}
);
}
问题是当引发异常时,不可能用
Problem is that when exception is thrown it is impossible to handle it with
@ExceptionHandler(CustomException.class)
public String handleException(CustomException exception) {
// log exception
return exception.getMessage();
}
Spring用BindException包装初始异常.该实例包含我最初的错误消息,但与其他文本串联在一起,这对我来说是多余的.我认为对该消息进行解析和子字符串化不是很好...
Spring wraps initial exception with BindException. That instance contains my initial error message, but concatenated with other text which is redundant for me. I don't think that parsing and substringing that message is good...
我错过了什么吗?从初始获取消息的正确方法是什么CustomException在这里吗?
Am I missing something? What is the proper way to get message from initialCustomException here?
推荐答案
在使用@ExceptionHandler
注释的方法进入控制器方法之前,您将无法处理引发的异常. Spring在进入控制器之前通过注册DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver
处理程序来处理这些异常.这是BindingException的情况,当Spring无法绑定请求参数以匹配您的InputDTO对象时,抛出该异常.您可以做的是注册自己的处理程序(创建实现HandlerExceptionResolver
和Ordered
接口的Component
),在处理错误时将其赋予最高优先级,并根据需要处理异常.您还必须注意BindException
,因为它包装了您的自定义异常CustomException.class
You will not be able to handle exceptions thrown before entering your controller method by using @ExceptionHandler
annotated methods. Spring handles these exceptions before entering the controller, by registering DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver
handler.This is the case of BindingException, thrown when Spring cannot bind request parameters to match your InputDTO object.What you can do is to register your own handler (create a Component
implementing HandlerExceptionResolver
and Ordered
interfaces), give it the highest priority in handling errors and play with exceptions as needed.You have also to pay attention to BindException
as it wrappes your custom exception, CustomException.class
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import yourpackage.CustomException;
@Component()
public class BindingExceptionResolver implements HandlerExceptionResolver, Ordered {
private static final Logger logger = LoggerFactory.getLogger(BindingExceptionResolver.class);
public BindingExceptionResolver() {
}
private ModelAndView handleException(ObjectError objectError, HttpServletResponse response){
if (objectError == null) return null;
try {
if(objectError.contains(CustomException.class)) {
CustomException ex = objectError.unwrap(CustomException.class);
logger.error(ex.getMessage(), ex);
return handleCustomException(ex, response);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return null;
}
protected ModelAndView handleCustomException(CustomException ex, HttpServletResponse response) throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
return new ModelAndView();
}
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
if (ex instanceof org.springframework.validation.BindException) {
BindException be = (BindException) ex;
logger.debug("Binding exception in {} :: ({}) :: ({})=({})", be.getObjectName(), be.getBindingResult().getTarget().getClass(), be.getFieldError().getField(), be.getFieldError().getRejectedValue());
return be.getAllErrors().stream()
.filter(o->o.contains(Exception.class))
.map(o ->handleException(o, response))
.filter(mv ->mv !=null)
.findFirst().orElse(null);
}
} catch (Exception handlerException) {
logger.error("Could not handle exception", handlerException);
}
return null;
}
@Override
public int getOrder() {
return Integer.MIN_VALUE;
}
}
希望有帮助
这篇关于Spring Boot-用BindException包装的异常处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!