#exception #java-8 #jackson #tomcat8 #spring-rest
Вопрос:
Я определил пользовательский JsonSerializer в своем веб-приложении spring (НЕ Spring Boot).
public class CalendarSerializer extends StdSerializer<Calendar>
{
public CalendarSerializer()
{
super(Calendar.class);
}
/** {@inheritDoc} */
@Override
public void serialize(Calendar value, JsonGenerator gen, SerializerProvider serializers) throws IOException
{
ZoneId zoneId = TimeZone.getDefault().toZoneId();
return value.toInstant().atZone(zoneId);
}
}
Он был включен в определение картографа json, и если в методе не возникает исключений serialize()
, все работает хорошо.
Чтобы справиться с ошибками, я создал ResponseEntityExceptionHandler
@ControllerAdvice
public class RestApiExceptionHandler extends ResponseEntityExceptionHandler
{
private static final Logger log = LoggerFactory.getLogger(RestApiExceptionHandler.class);
@ExceptionHandler(RestApiException.class)
protected ResponseEntity<RestApiErrorResponse> handleRestApiException(RestApiException ex)
{
RestApiErrorResponse apiErrorResponse = ex.getRestApiErrorResponse();
log.error("RestApiException occurs.", ex);
return new ResponseEntity<>(apiErrorResponse, apiErrorResponse.getHttpStatus());
}
[...]
@ExceptionHandler(Exception.class)
protected ResponseEntity<RestApiErrorResponse> handleUnexpectedError(Exception ex)
{
RestApiErrorResponse unexpectedError = RestApiErrorResponse.getUnexpectedError();
log.error("Unexpected exception. {}", unexpectedError, ex);
return new ResponseEntity<>(unexpectedError, unexpectedError.getHttpStatus());
}
}
Он обрабатывает некоторые пользовательские исключения, но в любом случае любой вид исключения управляется handleUnexpectedError
, и вызывающий правильно получает сериализацию RestApiErrorResponse.
Однако, если в системе возникает исключение CalendarSerializer
, ResponseEntityExceptionHandler
оно обходится, и вызывающий абонент получает страницу ошибки tomcat по умолчанию с трассировкой стека
Есть ли способ управлять исключением, вызванным JsonSerializers
так, чтобы вызывающий абонент получал пользовательский json с подробными сведениями об ошибке?
Вот трассировка стека
org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.foo.dataobjects.Holiday["startDate"])
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:296)
org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:103)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:290)
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:180)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:119)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
com.foo.rest.web.RestApiAuthenticationFilter.doFilter(RestApiAuthenticationFilter.java:122)
Root Cause
com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.foo.dataobjects.Holiday["startDate"])
com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:394)
com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:353)
com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:316)
com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:727)
com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:145)
com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:107)
com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25)
com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:400)
com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1392)
com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:913)
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:287)
org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:103)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:290)
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:180)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:119)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
com.foo.rest.web.RestApiAuthenticationFilter.doFilter(RestApiAuthenticationFilter.java:122)
Root Cause
java.lang.NullPointerException
com.foo.rest.serializers.CalendarSerializer.serialize(CalendarSerializer.java:44)
com.foo.rest.serializers.CalendarSerializer.serialize(CalendarSerializer.java:1)
com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer.serialize(StdDelegatingSerializer.java:168)
com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727)
com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:145)
com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:107)
com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25)
com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:400)
com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1392)
com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:913)
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:287)
org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:103)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:290)
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:180)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:119)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
com.foo.rest.web.RestApiAuthenticationFilter.doFilter(RestApiAuthenticationFilter.java:122)
ОБНОВЛЕНИЕ: Решено добавить в RestApiExceptionHandler
метод
@Override
protected ResponseEntity<Object> handleHttpMessageNotWritable(
HttpMessageNotWritableException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request)
{
String details = "Error exporting the results of the incoming request.";
RestApiErrorResponse error = new RestApiErrorResponse(details, RestApiErrorCode.SERIALIZATION_ERROR);
String uri = getUri(request);
log.error("Error serializing the results for request {}. {}", uri, error, ex);
return new ResponseEntity<>(error, error.getHttpStatus());
}
Комментарии:
1. API для определения
java.util
даты и времени устарел и подвержен ошибкам. Рекомендуется полностью прекратить его использование и переключиться на современный API даты и времени .2. Привет @ArvindKumarAvinash , я не искал способ исправить NPE, а также способ работы с календарями. NPE был искусственным, просто чтобы иметь исключение в JsonSerializer, Календари, к сожалению, обязательны для устаревшей проблемы. Решение, которое мне подходит, — это то, о котором я упоминал в разделе ОБНОВЛЕНИЯ.
3. Мора — Пожалуйста, опубликуйте свое обновление в качестве ответа. Через 48 часов вы даже сможете принять свой собственный ответ. Это будет полезно будущим посетителям этой страницы.
4. @ArvindKumarAvinash готово, спасибо
Ответ №1:
Решил сам, добавив в обработчик RestApiExceptionHandler метод
@Override
protected ResponseEntity<Object> handleHttpMessageNotWritable(
HttpMessageNotWritableException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request)
{
String details = "Error exporting the results of the incoming request.";
RestApiErrorResponse error = new RestApiErrorResponse(details, RestApiErrorCode.SERIALIZATION_ERROR);
String uri = getUri(request);
log.error("Error serializing the results for request {}. {}", uri, error, ex);
return new ResponseEntity<>(error, error.getHttpStatus());
}