DATAREST-421 - Polishing of new exception handling infrastructure.

Removed leftovers from AbstractRepositoryRestController. Renamed GlobalExceptionHandler to RepositoryRestExceptionHandler and minimized visibility of exception handler methods. Restricted application of the exception handler to controllers in the Spring Data REST base package.

Relatted pull request: #155.
This commit is contained in:
Oliver Gierke
2014-12-05 10:21:16 +01:00
parent e13be6badc
commit 28db79ef42
9 changed files with 317 additions and 261 deletions

View File

@@ -15,8 +15,12 @@
*/
package org.springframework.data.rest.webmvc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.springframework.data.rest.webmvc.ControllerUtils.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.rest.core.mapping.ResourceMetadata;
import org.springframework.data.web.PagedResourcesAssembler;
@@ -25,12 +29,6 @@ import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.springframework.data.rest.webmvc.ControllerUtils.EMPTY_RESOURCE_LIST;
/**
* @author Jon Brisbin
* @author Oliver Gierke
@@ -39,8 +37,6 @@ import static org.springframework.data.rest.webmvc.ControllerUtils.EMPTY_RESOURC
@SuppressWarnings({ "rawtypes" })
class AbstractRepositoryRestController {
private static final Logger LOG = LoggerFactory.getLogger(AbstractRepositoryRestController.class);
private final PagedResourcesAssembler<Object> pagedResourcesAssembler;
/**

View File

@@ -1,160 +0,0 @@
package org.springframework.data.rest.webmvc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.rest.core.RepositoryConstraintViolationException;
import org.springframework.data.rest.webmvc.support.ETagDoesntMatchException;
import org.springframework.data.rest.webmvc.support.ExceptionMessage;
import org.springframework.data.rest.webmvc.support.RepositoryConstraintViolationExceptionMessage;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.lang.reflect.InvocationTargetException;
import java.util.Locale;
/**
* @author Thibaud Lepretre
*/
@ControllerAdvice
public class GlobalExceptionHandler implements MessageSourceAware {
private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);
private MessageSourceAccessor messageSourceAccessor;
/*
* (non-Javadoc)
* @see org.springframework.context.MessageSourceAware#setMessageSource(org.springframework.context.MessageSource)
*/
@Override
public void setMessageSource(MessageSource messageSource) {
this.messageSourceAccessor = new MessageSourceAccessor(messageSource);
}
@ExceptionHandler({ NullPointerException.class })
@ResponseBody
public ResponseEntity<?> handleNPE(NullPointerException npe) {
return errorResponse(npe, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler({ ResourceNotFoundException.class })
@ResponseBody
public ResponseEntity<?> handleNotFound() {
return notFound();
}
@ExceptionHandler({ HttpMessageNotReadableException.class })
@ResponseBody
public ResponseEntity<ExceptionMessage> handleNotReadable(HttpMessageNotReadableException e) {
return badRequest(e);
}
/**
* Handle failures commonly thrown from code tries to read incoming data and convert or cast it to the right type.
*
* @param t
* @return
*/
@ExceptionHandler({ InvocationTargetException.class, IllegalArgumentException.class, ClassCastException.class,
ConversionFailedException.class })
@ResponseBody
public ResponseEntity handleMiscFailures(Throwable t) {
if (null != t.getCause() && t.getCause() instanceof ResourceNotFoundException) {
return notFound();
}
return badRequest(t);
}
@ExceptionHandler({ RepositoryConstraintViolationException.class })
@ResponseBody
public ResponseEntity handleRepositoryConstraintViolationException(Locale locale,
RepositoryConstraintViolationException rcve) {
return response(null, new RepositoryConstraintViolationExceptionMessage(rcve, messageSourceAccessor),
HttpStatus.BAD_REQUEST);
}
/**
* Send a 409 Conflict in case of concurrent modification.
*
* @param ex
* @return HTTP Status 409 ResponseEntity
*/
@ExceptionHandler({ OptimisticLockingFailureException.class, DataIntegrityViolationException.class })
public ResponseEntity handleConflict(Exception ex) {
return errorResponse(null, ex, HttpStatus.CONFLICT);
}
/**
* Send {@code 405 Method Not Allowed} and include the supported {@link org.springframework.http.HttpMethod}s in the {@code Allow} header.
*
* @param o_O
* @return HTTP Status 405 ResponseEntity
*/
@ExceptionHandler
public ResponseEntity<Void> handle(HttpRequestMethodNotSupportedException o_O) {
HttpHeaders headers = new HttpHeaders();
headers.setAllow(o_O.getSupportedHttpMethods());
return new ResponseEntity<Void>(headers, HttpStatus.METHOD_NOT_ALLOWED);
}
@ExceptionHandler
public ResponseEntity<Void> handle(ETagDoesntMatchException o_O) {
HttpHeaders headers = o_O.getExpectedETag().addTo(new HttpHeaders());
return new ResponseEntity<Void>(headers, HttpStatus.PRECONDITION_FAILED);
}
protected <T> ResponseEntity<T> notFound() {
return notFound(null, null);
}
protected <T> ResponseEntity<T> notFound(HttpHeaders headers, T body) {
return response(headers, body, HttpStatus.NOT_FOUND);
}
protected <T extends Throwable> ResponseEntity<ExceptionMessage> badRequest(T throwable) {
return badRequest(null, throwable);
}
protected <T extends Throwable> ResponseEntity<ExceptionMessage> badRequest(HttpHeaders headers, T throwable) {
return errorResponse(headers, throwable, HttpStatus.BAD_REQUEST);
}
public <T extends Throwable> ResponseEntity<ExceptionMessage> errorResponse(T throwable, HttpStatus status) {
return errorResponse(null, throwable, status);
}
public <T extends Throwable> ResponseEntity<ExceptionMessage> errorResponse(HttpHeaders headers, T throwable,
HttpStatus status) {
if (null != throwable && null != throwable.getMessage()) {
LOG.error(throwable.getMessage(), throwable);
return response(headers, new ExceptionMessage(throwable), status);
} else {
return response(headers, null, status);
}
}
public <T> ResponseEntity<T> response(HttpHeaders headers, T body, HttpStatus status) {
HttpHeaders hdrs = new HttpHeaders();
if (null != headers) {
hdrs.putAll(headers);
}
return new ResponseEntity<T>(body, hdrs, status);
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.rest.webmvc;
import java.lang.reflect.InvocationTargetException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.rest.core.RepositoryConstraintViolationException;
import org.springframework.data.rest.webmvc.support.ETagDoesntMatchException;
import org.springframework.data.rest.webmvc.support.ExceptionMessage;
import org.springframework.data.rest.webmvc.support.RepositoryConstraintViolationExceptionMessage;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.Assert;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* Exception handler for Spring Data REST controllers.
*
* @author Thibaud Lepretre
* @author Oliver Gierke
*/
@ControllerAdvice(basePackageClasses = RepositoryRestExceptionHandler.class)
public class RepositoryRestExceptionHandler {
private static final Logger LOG = LoggerFactory.getLogger(RepositoryRestExceptionHandler.class);
private final MessageSourceAccessor messageSourceAccessor;
/**
* Creates a new {@link RepositoryRestExceptionHandler} using the given {@link MessageSource}.
*
* @param messageSource must not be {@literal null}.
*/
public RepositoryRestExceptionHandler(MessageSource messageSource) {
Assert.notNull(messageSource, "MessageSource must not be null!");
this.messageSourceAccessor = new MessageSourceAccessor(messageSource);
}
/**
* Handles {@link ResourceNotFoundException} by returning {@code 404 Not Found}.
*
* @param o_O the exception to handle.
* @return
*/
@ExceptionHandler
ResponseEntity<?> handleNotFound(ResourceNotFoundException o_O) {
return notFound();
}
/**
* Handles {@link HttpMessageNotReadableException} by returning {@code 400 Bad Request}.
*
* @param o_O the exception to handle.
* @return
*/
@ExceptionHandler
ResponseEntity<ExceptionMessage> handleNotReadable(HttpMessageNotReadableException o_O) {
return badRequest(o_O);
}
/**
* Handle failures commonly thrown from code tries to read incoming data and convert or cast it to the right type by
* returning {@code 500 Internal Server Error} and the thrown exception marshalled into JSON.
*
* @param o_O the exception to handle.
* @return
*/
@ExceptionHandler({ InvocationTargetException.class, IllegalArgumentException.class, ClassCastException.class,
ConversionFailedException.class, NullPointerException.class })
ResponseEntity<ExceptionMessage> handleMiscFailures(Exception o_O) {
return errorResponse(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
/**
* Handles {@link RepositoryConstraintViolationException}s by returning {@code 400 Bad Request}.
*
* @param o_O the exception to handle.
* @return
*/
@ExceptionHandler
ResponseEntity<RepositoryConstraintViolationExceptionMessage> handleRepositoryConstraintViolationException(
RepositoryConstraintViolationException o_O) {
return response(new HttpHeaders(), new RepositoryConstraintViolationExceptionMessage(o_O, messageSourceAccessor),
HttpStatus.BAD_REQUEST);
}
/**
* Send a {@code 409 Conflict} in case of concurrent modification.
*
* @param o_O the exception to handle.
* @return
*/
@ExceptionHandler({ OptimisticLockingFailureException.class, DataIntegrityViolationException.class })
ResponseEntity<ExceptionMessage> handleConflict(Exception o_O) {
return errorResponse(null, o_O, HttpStatus.CONFLICT);
}
/**
* Send {@code 405 Method Not Allowed} and include the supported {@link org.springframework.http.HttpMethod}s in the
* {@code Allow} header.
*
* @param o_O the exception to handle.
* @return
*/
@ExceptionHandler
ResponseEntity<Void> handle(HttpRequestMethodNotSupportedException o_O) {
HttpHeaders headers = new HttpHeaders();
headers.setAllow(o_O.getSupportedHttpMethods());
return new ResponseEntity<Void>(headers, HttpStatus.METHOD_NOT_ALLOWED);
}
/**
* Handles {@link ETagDoesntMatchException} by returning {@code 412 Precondition Failed}.
*
* @param o_O the exception to handle.
* @return
*/
@ExceptionHandler
ResponseEntity<Void> handle(ETagDoesntMatchException o_O) {
HttpHeaders headers = o_O.getExpectedETag().addTo(new HttpHeaders());
return new ResponseEntity<Void>(headers, HttpStatus.PRECONDITION_FAILED);
}
private <T> ResponseEntity<T> notFound() {
return notFound(new HttpHeaders(), null);
}
private <T> ResponseEntity<T> notFound(HttpHeaders headers, T body) {
return response(headers, body, HttpStatus.NOT_FOUND);
}
private <T extends Exception> ResponseEntity<ExceptionMessage> badRequest(T throwable) {
return badRequest(null, throwable);
}
private <T extends Exception> ResponseEntity<ExceptionMessage> badRequest(HttpHeaders headers, T throwable) {
return errorResponse(headers, throwable, HttpStatus.BAD_REQUEST);
}
private <T extends Exception> ResponseEntity<ExceptionMessage> errorResponse(T throwable, HttpStatus status) {
return errorResponse(new HttpHeaders(), throwable, status);
}
private <T extends Exception> ResponseEntity<ExceptionMessage> errorResponse(HttpHeaders headers,
Exception exception, HttpStatus status) {
if (null != exception && null != exception.getMessage()) {
LOG.error(exception.getMessage(), exception);
return response(headers, new ExceptionMessage(exception), status);
} else {
return response(headers, null, status);
}
}
public <T> ResponseEntity<T> response(HttpHeaders headers, T body, HttpStatus status) {
Assert.notNull(headers, "Headers must not be null!");
Assert.notNull(status, "HttpStatus must not be null!");
return new ResponseEntity<T>(body, headers, status);
}
}

View File

@@ -25,7 +25,6 @@ import java.util.Set;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -68,8 +67,8 @@ import org.springframework.data.rest.core.support.RepositoryRelProvider;
import org.springframework.data.rest.webmvc.BaseUri;
import org.springframework.data.rest.webmvc.BaseUriAwareController;
import org.springframework.data.rest.webmvc.BaseUriAwareHandlerMapping;
import org.springframework.data.rest.webmvc.GlobalExceptionHandler;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.data.rest.webmvc.RepositoryRestExceptionHandler;
import org.springframework.data.rest.webmvc.RepositoryRestHandlerAdapter;
import org.springframework.data.rest.webmvc.RepositoryRestHandlerMapping;
import org.springframework.data.rest.webmvc.RestMediaTypes;
@@ -146,7 +145,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
private static final boolean IS_JPA_AVAILABLE = ClassUtils.isPresent("javax.persistence.EntityManager",
RepositoryRestMvcConfiguration.class.getClassLoader());
@Autowired ListableBeanFactory beanFactory;
@Autowired ApplicationContext applicationContext;
@Autowired(required = false) List<BackendIdConverter> idConverters = Collections.emptyList();
@@ -155,7 +154,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
@Bean
public Repositories repositories() {
return new Repositories(beanFactory);
return new Repositories(applicationContext);
}
@Bean
@@ -168,7 +167,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
List<MappingContext<?, ?>> arrayList = new ArrayList<MappingContext<?, ?>>();
for (MappingContext<?, ?> context : BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory,
for (MappingContext<?, ?> context : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,
MappingContext.class).values()) {
arrayList.add(context);
}
@@ -474,7 +473,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
List<HttpMessageConverter<?>> messageConverters = defaultMessageConverters();
configureHttpMessageConverters(messageConverters);
Collection<ResourceProcessor> beans = beanFactory.getBeansOfType(ResourceProcessor.class, false, false).values();
Collection<ResourceProcessor> beans = applicationContext.getBeansOfType(ResourceProcessor.class, false, false)
.values();
List<ResourceProcessor<?>> processors = new ArrayList<ResourceProcessor<?>>(beans.size());
for (ResourceProcessor<?> bean : beans) {
@@ -549,8 +549,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
}
@Bean
public GlobalExceptionHandler globalExceptionHandler() {
return new GlobalExceptionHandler();
public RepositoryRestExceptionHandler repositoryRestExceptionHandler() {
return new RepositoryRestExceptionHandler(applicationContext);
}
@Bean
@@ -628,8 +628,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
private List<HandlerMethodArgumentResolver> defaultMethodArgumentResolvers() {
PersistentEntityResourceAssemblerArgumentResolver peraResolver = new PersistentEntityResourceAssemblerArgumentResolver(
repositories(), entityLinks(), config().projectionConfiguration(), new ProxyProjectionFactory(beanFactory),
resourceMappings());
repositories(), entityLinks(), config().projectionConfiguration(), new ProxyProjectionFactory(
applicationContext), resourceMappings());
HateoasPageableHandlerMethodArgumentResolver pageableResolver = pageableResolver();
HandlerMethodArgumentResolver defaultedPageableResolver = new DefaultedPageableHandlerMethodArgumentResolver(

View File

@@ -9,23 +9,19 @@ import com.fasterxml.jackson.annotation.JsonProperty;
*/
public class ExceptionMessage {
private final Throwable exception;
private final Throwable throwable;
public ExceptionMessage(Throwable exception) {
this.exception = exception;
public ExceptionMessage(Throwable throwable) {
this.throwable = throwable;
}
@JsonProperty("message")
public String getMessage() {
return exception.getMessage();
return throwable.getMessage();
}
@JsonProperty("cause")
public ExceptionMessage getCause() {
if (null != exception.getCause()) {
return new ExceptionMessage(exception.getCause());
}
return null;
return throwable.getCause() != null ? new ExceptionMessage(throwable.getCause()) : null;
}
}

View File

@@ -1,3 +1,18 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.rest.webmvc.support;
import java.util.ArrayList;
@@ -11,6 +26,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author Jon Brisbin
* @author Oliver Gierke
*/
public class RepositoryConstraintViolationExceptionMessage {
@@ -21,17 +37,8 @@ public class RepositoryConstraintViolationExceptionMessage {
for (FieldError fieldError : violationException.getErrors().getFieldErrors()) {
List<Object> args = new ArrayList<Object>();
args.add(fieldError.getObjectName());
args.add(fieldError.getField());
args.add(fieldError.getRejectedValue());
if (null != fieldError.getArguments()) {
for (Object o : fieldError.getArguments()) {
args.add(o);
}
}
String message = accessor.getMessage(fieldError);
String message = accessor.getMessage(fieldError.getCode(), args.toArray(), fieldError.getDefaultMessage());
this.errors.add(new ValidationError(fieldError.getObjectName(), message, String.format("%s",
fieldError.getRejectedValue()), fieldError.getField()));
}
@@ -72,5 +79,4 @@ public class RepositoryConstraintViolationExceptionMessage {
return property;
}
}
}

View File

@@ -1,32 +0,0 @@
package org.springframework.data.rest.webmvc.support;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* @author Thibaud Lepretre
*/
@Configuration
@Import(JpaRepositoryConfig.class)
public class ControllerAdviceConfig {
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public static class CustomGlobalConfiguration {
@ExceptionHandler
public ResponseEntity<Void> handle(HttpRequestMethodNotSupportedException o_O) {
HttpHeaders headers = new HttpHeaders();
headers.setAllow(o_O.getSupportedHttpMethods());
return new ResponseEntity<Void>(headers, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

View File

@@ -1,29 +0,0 @@
package org.springframework.data.rest.webmvc.support;
import org.junit.Test;
import org.springframework.data.rest.webmvc.AbstractWebIntegrationTests;
import org.springframework.hateoas.Link;
import org.springframework.test.context.ContextConfiguration;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author Thibaud Lepretre
*/
@ContextConfiguration(classes = ControllerAdviceConfig.class)
public class ControllerAdviceWebTests extends AbstractWebIntegrationTests {
@Test
public void httpRequestMethodNotSupportedExceptionShouldNowReturnHttpStatus500Over405() throws Exception {
Link link = client.discoverUnique("addresses");
mvc.perform(get(link.getHref())).//
andExpect(status().isInternalServerError());
}
@Override
protected Iterable<String> expectedRootLinkRels() {
return null;
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.rest.webmvc.support;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.Collections;
import org.junit.Test;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.data.rest.webmvc.AbstractWebIntegrationTests;
import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
import org.springframework.hateoas.Link;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* Integration tests for customization of Spring Data REST's exception handling.
*
* @author Thibaud Lepretre
* @author Oliver Gierke
*/
@ContextConfiguration
public class ExceptionHandlingCustomizationIntegrationTests extends AbstractWebIntegrationTests {
@Configuration
@Import(JpaRepositoryConfig.class)
static class ControllerAdviceConfig {
@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
static class CustomGlobalConfiguration {
@ExceptionHandler
ResponseEntity<Void> handle(HttpRequestMethodNotSupportedException o_O) {
HttpHeaders headers = new HttpHeaders();
headers.setAllow(o_O.getSupportedHttpMethods());
return new ResponseEntity<Void>(headers, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
@Test
public void httpRequestMethodNotSupportedExceptionShouldNowReturnHttpStatus500Over405() throws Exception {
Link link = client.discoverUnique("addresses");
mvc.perform(get(link.getHref())).//
andExpect(status().isInternalServerError());
}
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.AbstractWebIntegrationTests#expectedRootLinkRels()
*/
@Override
protected Iterable<String> expectedRootLinkRels() {
return Collections.emptySet();
}
}