使用自定义消息捕获和处理 Jackson 异常
Posted
技术标签:
【中文标题】使用自定义消息捕获和处理 Jackson 异常【英文标题】:Catching & Handling Jackson Exceptions with a custom message 【发布时间】:2019-01-02 07:14:11 【问题描述】:我希望能捕捉到我正在开发的 spring-boot API 中发生的一些 jackson 异常。例如,我有以下请求类,我想捕获当 JSON 请求对象中的“questionnaireResponse”键为空或空白时发生的错误,即请求对象中的" "
。
@Validated
@JsonRootName("questionnaireResponse")
public class QuestionnaireResponse
@JsonProperty("identifier")
@Valid
private Identifier identifier = null;
@JsonProperty("basedOn")
@Valid
private List<Identifier_WRAPPED> basedOn = null;
@JsonProperty("parent")
@Valid
private List<Identifier_WRAPPED> parent = null;
@JsonProperty("questionnaire")
@NotNull(message = "40000")
@Valid
private Identifier_WRAPPED questionnaire = null;
@JsonProperty("status")
@NotNull(message = "40000")
@NotEmptyString(message = "40005")
private String status = null;
@JsonProperty("subject")
@Valid
private Identifier_WRAPPED subject = null;
@JsonProperty("context")
@Valid
private Identifier_WRAPPED context = null;
@JsonProperty("authored")
@NotNull(message = "40000")
@NotEmptyString(message = "40005")
@Pattern(regexp = "\\d4-(?:0[1-9]|[1-2]\\d|3[0-1])-(?:0[1-9]|1[0-2])T(?:[0-1]\\d|2[0-3]):[0-5]\\d:[0-5]\\dZ", message = "40001")
private String authored;
@JsonProperty("author")
@NotNull(message = "40000")
@Valid
private QuestionnaireResponseAuthor author = null;
@JsonProperty("source")
@NotNull(message = "40000")
@Valid
private Identifier_WRAPPED source = null; // Reference(Patient | Practitioner | RelatedPerson) resources not implemented
@JsonProperty("item")
@NotNull(message = "40000")
@Valid
private List<QuestionnaireResponseItem> item = null;
public Identifier getIdentifier()
return identifier;
public void setIdentifier(Identifier identifier)
this.identifier = identifier;
public List<Identifier_WRAPPED> getBasedOn()
return basedOn;
public void setBasedOn(List<Identifier_WRAPPED> basedOn)
this.basedOn = basedOn;
public List<Identifier_WRAPPED> getParent()
return parent;
public void setParent(List<Identifier_WRAPPED> parent)
this.parent = parent;
public Identifier_WRAPPED getQuestionnaire()
return questionnaire;
public void setQuestionnaire(Identifier_WRAPPED questionnaire)
this.questionnaire = questionnaire;
public String getStatus()
return status;
public void setStatus(String status)
this.status = status;
public Identifier_WRAPPED getSubject()
return subject;
public void setSubject(Identifier_WRAPPED subject)
this.subject = subject;
public Identifier_WRAPPED getContext()
return context;
public void setContext(Identifier_WRAPPED context)
this.context = context;
public String getAuthored()
return authored;
public void setAuthored(String authored)
this.authored = authored;
public QuestionnaireResponseAuthor getAuthor()
return author;
public void setAuthor(QuestionnaireResponseAuthor author)
this.author = author;
public Identifier_WRAPPED getSource()
return source;
public void setSource(Identifier_WRAPPED source)
this.source = source;
public List<QuestionnaireResponseItem> getItem()
return item;
public void setItem(List<QuestionnaireResponseItem> item)
this.item = item;
导致此 Jackson 错误:
"Map":
"timestamp": "2018-07-25T12:45:32.285Z",
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Root name '' does not match expected ('questionnaireResponse') for type [simple type, class com.optum.genomix.model.gel.QuestionnaireResponse]; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Root name '' does not match expected ('questionnaireResponse') for type [simple type, class com.optum.genomix.model.gel.QuestionnaireResponse]\n at [Source: (PushbackInputStream); line: 2, column: 3]",
"path": "/api/optumhealth/genomics/v1.0/questionnaireResponse/create"
有没有办法捕捉和处理这些异常(在示例中 JsonRootName 为空/无效),可能类似于扩展 ResponseEntityExceptionHandler 的 @ControllerAdvice 类?
【问题讨论】:
Here is a solution for Quarkus users. 【参考方案1】:尝试以下方式:
@ControllerAdvice
public class ExceptionConfiguration extends ResponseEntityExceptionHandler
@ExceptionHandler(JsonMappingException.class) // Or whatever exception type you want to handle
public ResponseEntity<SomeErrorResponsePojo> handleConverterErrors(JsonMappingException exception) // Or whatever exception type you want to handle
return ResponseEntity.status(...).body(...your response pojo...).build();
这允许您处理任何类型的异常并做出相应的响应。如果响应状态始终相同,只需在方法上粘贴 @ResponseStatus(HttpStatus.some_status)
并调用 ResponseEntity.body(...)
【讨论】:
感谢您的回复!我已经尝试过使用 Exception import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;但是,在调用异常时,处理程序方法永远不会被捕获或执行.. @Broncos423 尝试更改为仅映射“RuntimeException.class”并在处理程序中进行调试,我发现很多异常都包含在 Spring 异常中 是否有与 spring 无关的方法来提取自定义异常?【参考方案2】:您可以执行以下操作:
@ExceptionHandler(HttpMessageNotReadableException.class)
public CustomResponse handleJsonException(HttpServletResponse response, HttpMessageNotReadableException ex)
return customGenericResponse(ex);
public CustomResponse customGenericResponse(HttpMessageNotReadableException ex)
//here build your custom response
CustomResponse customResponse = new CustomResponse();
GenericError error = new GenericError();
error.setMessage(ex.getMessage());
error.setCode(500);
customResponse.setError(error);
return customResponse;
CustomResponse 将是:
public class CustomResponse
Object data;
GenericError error;
public class GenericError
private Integer code;
private String message;
在 customGenericResponse 中,您可以检查 instanceOf 导致 ex 的原因并相应地返回您的自定义错误消息。
【讨论】:
【参考方案3】:发现这个问题有一个类似的问题,只有我的是一个不同的 JSON 解析错误:
JSON parse error: Unrecognized character escape 'w' (code 119); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unrecognized character escape 'w' (code 119)\n at [Source: (PushbackInputStream); line: 1, column: 10]
来自这样的 REST JSON 请求
"query":"\\w"
如果您可以修改 Rest Controller,则可以使用 HttpMessageNotReadableException
捕获 JSON 解析错误(在 Spring Boot 中使用 @RestController
注释为我工作)。即使我无法通过@ExceptionHandler(Exception.class)
发现错误
您可以使用序列化对象(自然转换为 JSON)以自定义 JSON 响应。您还可以指定首先需要导致问题的请求和异常。这样您就可以获取详细信息,或者修改错误消息。
@ResponseBody
@ExceptionHandler(HttpMessageNotReadableException.class)
private SerializableResponseObject badJsonRequestHandler(HttpServletRequest req, Exception ex)
SerializableResponseObject response = new SerializableResponseObject(404,
"Bad Request",
"Invalid request parameters, could not create query",
req.getRequestURL().toString())
Logger logger = LoggerFactory.getLogger(UserController.class);
logger.error("Exception: \t\t", response);
return response;
代码会返回类似
"timestamp": "Thu Oct 17 10:19:48 PDT 2019",
"status": 404,
"error": "Bad Request",
"message": "Invalid request parameters, could not create query",
"path": "http://localhost:8080/user/query"
并且会记录类似的东西
Exception: [Thu Oct 17 10:19:48 PDT 2019][404][http://localhost:8080/user/query][Bad Request]: Invalid request parameters, could not create query
SerializableResponseObject 的代码
public class SerializableResponseObject implements Serializable
public String timestamp;
public Integer status;
public String error;
public String message;
public String path;
public SerializableResponseObject(Integer status, String error, String message, String path)
this.timestamp = (new Date()).toString();
this.status = status;
this.error = error;
this.message = message;
this.path = path;
public String getTimestamp()
return timestamp;
public Integer getStatus()
return status;
public String getError()
return error;
public String getMessage()
return message;
public String getPath()
return path;
public void setTimestamp(String timestamp)
this.timestamp = timestamp;
public void setStatus(Integer status)
this.status = status;
public void setError(String error)
this.error = error;
public void setMessage(String message)
this.message = message;
public void setPath(String path)
this.path = path;
public String toString()
return "[" + this.timestamp + "][" + this.status + "][" + this.path + "][" + this.error + "]: " + this.message;
【讨论】:
【参考方案4】:是的,你可以实现它实现 HandlerIntercepter。有了这个,您可以预先处理请求 && 如果您想提供自定义消息,然后使用 @ControllerAdvice 处理异常。
public class CustomInterceptor implements HandlerInterceptor
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
//your custom logic here.
return true;
你需要配置这个拦截器:
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter
@Override
public void addInterceptors(InterceptorRegistry registry)
registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/**");
这里是处理异常:
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class GlobalExceptionHandler
private static final Logger logger = LogManager.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(JsonProcessingException.class)
public void handleJsonException(HttpServletResponse response, Exception ex)
//here build your custom response
prepareErrorResponse(response,UNPROCESSABLE_ENTITY,"");
private void prepareErrorResponse(HttpServletResponse response, HttpStatus status, String apiError)
response.setStatus(status.value());
try(PrintWriter writer = response.getWriter())
new ObjectMapper().writeValue(writer, apiError);
catch (IOException ex)
logger.error("Error writing string to response body", ex);
【讨论】:
嗯,这看起来和我正在寻找的完全一样 - 但我只是有点困惑在 preHandle 方法中放入什么......这就像一个 try/catch 映射请求一个对象?还是我错过了这条船(我只想捕获并针对某些杰克逊数据绑定错误抛出自定义错误消息).. 如果要数据绑定错误,则在 GlobalExceptionalHandler 类的 handleJsonException 中构建消息 在预处理方法中。无事可做 不起作用,我根本无法访问 ControllerAdvice!以上是关于使用自定义消息捕获和处理 Jackson 异常的主要内容,如果未能解决你的问题,请参考以下文章
用于检索自定义消息而不是异常详细信息的 PLSQL 异常处理
Feign 自定义 ErrorDecoder (捕获 Feign 服务端异常)
Spring boot异常统一处理方法:@ControllerAdvice注解的使用全局异常捕获自定义异常捕获