Я надеюсь отловить некоторые исключения Джексона, которые возникают в разрабатываемом мной API для весенней загрузки. Например, у меня есть следующий класс запроса, и я хочу поймать ошибку, которая возникает, когда ключ "questionnaireResponse" в объекте запроса JSON имеет значение null или пусто, то есть " " в объекте запроса.
@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 = "\\d{4}-(?: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;
}
}
Это приводит к этой ошибке Джексона:
{
"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 имеет значение null / недействительно), возможно, аналогично классам @ControllerAdvice, расширяющим ResponseEntityExceptionHandler?




Да, вы можете это сделать, реализует Обработчик. С его помощью вы можете предварительно обработать запрос &&, если вы хотите передать свое собственное сообщение, обработайте исключение с помощью @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 ... что это может быть что-то вроде попытки / улова сопоставления запроса с объектом? или я скучаю по лодке (я просто хочу поймать и выдать собственное сообщение об ошибке для определенных ошибок привязки данных Джексона) ..
если вы хотите привязать ошибку к данным, создайте сообщение в handleJsonException в классе GlobalExceptionalHandler
в методе предварительной обработки. нечего делать
не работает, я вообще не могу связаться с ControllerAdvice!
Попробуйте что-нибудь вроде:
@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
Будет ли способ извлечения настраиваемого исключения без привязки к пружинам?
Вы можете сделать что-то вроде ниже:
@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, причину бывший, и соответственно вернуть свои собственные сообщения об ошибках.
Нашел этот вопрос с аналогичной проблемой, только у меня была другая ошибка синтаксического анализа 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, вы можете поймать ошибку синтаксического анализа JSON с помощью HttpMessageNotReadableException (работал у меня в 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;
}
}