Added missing exception resolver configuration to turn exceptions into meaningful HTTP codes as well as provide sane serialization of exception stacktraces.
This commit is contained in:
@@ -22,7 +22,7 @@ public class RepositoryRestConfiguration {
|
||||
private String jsonpOnErrParamName = null;
|
||||
private List<HttpMessageConverter<?>> customConverters = Collections.emptyList();
|
||||
private MediaType defaultMediaType = MediaType.APPLICATION_JSON;
|
||||
private boolean dumpErrors = false;
|
||||
private boolean dumpErrors = true;
|
||||
|
||||
public int getDefaultPageSize() {
|
||||
return defaultPageSize;
|
||||
|
||||
@@ -31,7 +31,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -1367,38 +1366,8 @@ public class RepositoryRestController
|
||||
@ResponseBody
|
||||
public ResponseEntity handleNPE(NullPointerException e,
|
||||
ServletServerHttpRequest request) throws IOException {
|
||||
if(LOG.isErrorEnabled()) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
return negotiateResponse(request, HttpStatus.INTERNAL_SERVER_ERROR, new HttpHeaders(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle {@link InvocationTargetException}s as a 400 Bad Request because these are likely to occur if, e.g. the user
|
||||
* does not provide a value for a query parameter.
|
||||
*
|
||||
* @param e
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@ExceptionHandler(InvocationTargetException.class)
|
||||
@ResponseBody
|
||||
public ResponseEntity handleInvocationTargetException(InvocationTargetException e,
|
||||
ServletServerHttpRequest request) throws IOException {
|
||||
if(LOG.isErrorEnabled()) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
for(Throwable cause = e.getCause(); (null != cause && cause != e.getCause()); cause = cause.getCause()) {
|
||||
if(cause instanceof InvalidDataAccessApiUsageException || cause instanceof IllegalArgumentException) {
|
||||
return negotiateResponse(request, HttpStatus.BAD_REQUEST, new HttpHeaders(), null);
|
||||
}
|
||||
}
|
||||
|
||||
return negotiateResponse(request, HttpStatus.INTERNAL_SERVER_ERROR, new HttpHeaders(), e);
|
||||
LOG.error(e.getMessage(), e);
|
||||
return errorResponse(request, HttpStatus.INTERNAL_SERVER_ERROR, e);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1413,6 +1382,7 @@ public class RepositoryRestController
|
||||
*/
|
||||
@ExceptionHandler(
|
||||
{
|
||||
InvocationTargetException.class,
|
||||
IllegalArgumentException.class,
|
||||
ClassCastException.class
|
||||
}
|
||||
@@ -1420,10 +1390,8 @@ public class RepositoryRestController
|
||||
@ResponseBody
|
||||
public ResponseEntity handleMiscFailures(Throwable t,
|
||||
ServletServerHttpRequest request) throws IOException {
|
||||
if(LOG.isErrorEnabled()) {
|
||||
LOG.error(t.getMessage(), t);
|
||||
}
|
||||
return negotiateResponse(request, HttpStatus.BAD_REQUEST, new HttpHeaders(), t);
|
||||
LOG.error(t.getMessage(), t);
|
||||
return errorResponse(request, HttpStatus.BAD_REQUEST, t);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1442,11 +1410,7 @@ public class RepositoryRestController
|
||||
public ResponseEntity handleLockingFailure(OptimisticLockingFailureException ex,
|
||||
ServletServerHttpRequest request) throws IOException {
|
||||
LOG.error(ex.getMessage(), ex);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
Map m = new HashMap();
|
||||
m.put("message", ex.getMessage());
|
||||
return negotiateResponse(request, HttpStatus.CONFLICT, headers, objectMapper.writeValueAsBytes(m));
|
||||
return errorResponse(request, HttpStatus.CONFLICT, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1493,13 +1457,11 @@ public class RepositoryRestController
|
||||
ServletServerHttpRequest request) throws IOException {
|
||||
LOG.error(ex.getMessage(), ex);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
Map m = new HashMap();
|
||||
m.put("message", ex.getMessage());
|
||||
m.put("acceptableTypes", availableMediaTypes);
|
||||
|
||||
return negotiateResponse(request, HttpStatus.BAD_REQUEST, headers, objectMapper.writeValueAsBytes(m));
|
||||
return negotiateResponse(request, HttpStatus.BAD_REQUEST, new HttpHeaders(), m);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1657,10 +1619,30 @@ public class RepositoryRestController
|
||||
&& !config.getSortParamName().equals(name));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private Map throwableToMap(Throwable t) {
|
||||
Map m = new HashMap();
|
||||
m.put("message", t.getMessage());
|
||||
if(null != t.getCause()) {
|
||||
m.put("cause", throwableToMap(t.getCause()));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
private ResponseEntity<byte[]> notFoundResponse(ServletServerHttpRequest request) throws IOException {
|
||||
return negotiateResponse(request, HttpStatus.NOT_FOUND, new HttpHeaders(), null);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private ResponseEntity<byte[]> errorResponse(ServletServerHttpRequest request, HttpStatus status, Throwable t)
|
||||
throws IOException {
|
||||
Object body = null;
|
||||
if(config.isDumpErrors()) {
|
||||
body = throwableToMap(t);
|
||||
}
|
||||
return negotiateResponse(request, status, new HttpHeaders(), body);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private ResponseEntity<byte[]> negotiateResponse(final ServletServerHttpRequest request,
|
||||
final HttpStatus status,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -7,7 +9,8 @@ import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.data.rest.repository.context.ValidatingRepositoryEventListener;
|
||||
import org.springframework.data.rest.repository.jpa.JpaRepositoryExporter;
|
||||
import org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor;
|
||||
import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
@@ -58,4 +61,12 @@ public class RepositoryRestMvcConfiguration {
|
||||
return new RepositoryRestHandlerMapping();
|
||||
}
|
||||
|
||||
@Bean public ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver() {
|
||||
ExceptionHandlerExceptionResolver er = new ExceptionHandlerExceptionResolver();
|
||||
er.setCustomArgumentResolvers(
|
||||
Arrays.<HandlerMethodArgumentResolver>asList(new ServerHttpRequestMethodArgumentResolver())
|
||||
);
|
||||
return er;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.AbstractHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class ThrowableHttpMessageConverter extends AbstractHttpMessageConverter<Throwable> {
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Override protected boolean supports(Class<?> clazz) {
|
||||
throw new IllegalStateException("supports(Class<?> clazz) not used in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override public boolean canRead(Class<?> clazz, MediaType mediaType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean canWrite(Class<?> clazz, MediaType mediaType) {
|
||||
return (Throwable.class.isAssignableFrom(clazz)
|
||||
&& (mediaType.getSubtype().contains("json") || mediaType.getSubtype().contains("text")));
|
||||
}
|
||||
|
||||
@Override protected Throwable readInternal(Class<? extends Throwable> clazz, HttpInputMessage inputMessage)
|
||||
throws IOException, HttpMessageNotReadableException {
|
||||
throw new HttpMessageNotReadableException("Cannot read Throwables from input.");
|
||||
}
|
||||
|
||||
@Override protected void writeInternal(Throwable throwable, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
if(outputMessage.getHeaders().getContentType().getSubtype().contains("json")) {
|
||||
outputMessage.getBody().write(mapper.writeValueAsBytes(throwable));
|
||||
} else {
|
||||
throwable.printStackTrace(new PrintWriter(outputMessage.getBody()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user