custom error message
This commit is contained in:
parent
24de497e5b
commit
d0d0406ae7
|
@ -139,6 +139,25 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- test scoped -->
|
<!-- test scoped -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-test</artifactId>
|
||||||
|
<version>${org.springframework.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jayway.restassured</groupId>
|
||||||
|
<artifactId>rest-assured</artifactId>
|
||||||
|
<version>${rest-assured.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>commons-logging</artifactId>
|
||||||
|
<groupId>commons-logging</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
|
@ -276,6 +295,7 @@
|
||||||
<org.hamcrest.version>1.3</org.hamcrest.version>
|
<org.hamcrest.version>1.3</org.hamcrest.version>
|
||||||
<junit.version>4.12</junit.version>
|
<junit.version>4.12</junit.version>
|
||||||
<mockito.version>1.10.19</mockito.version>
|
<mockito.version>1.10.19</mockito.version>
|
||||||
|
<rest-assured.version>2.4.1</rest-assured.version>
|
||||||
|
|
||||||
<!-- swagger -->
|
<!-- swagger -->
|
||||||
<springfox-swagger.version>2.2.2</springfox-swagger.version>
|
<springfox-swagger.version>2.2.2</springfox-swagger.version>
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package org.baeldung.web;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
public class ApiError {
|
||||||
|
|
||||||
|
private HttpStatus status;
|
||||||
|
private String message;
|
||||||
|
private List<String> errors;
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
public ApiError() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiError(final HttpStatus status, final String message, final List<String> errors) {
|
||||||
|
super();
|
||||||
|
this.status = status;
|
||||||
|
this.message = message;
|
||||||
|
this.errors = errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiError(final HttpStatus status, final String message, final String error) {
|
||||||
|
super();
|
||||||
|
this.status = status;
|
||||||
|
this.message = message;
|
||||||
|
errors = Arrays.asList(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
public HttpStatus getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(final HttpStatus status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(final String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
public List<String> getErrors() {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrors(final List<String> errors) {
|
||||||
|
this.errors = errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError(final String error) {
|
||||||
|
errors = Arrays.asList(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
package org.baeldung.web;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.beans.TypeMismatchException;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.validation.FieldError;
|
||||||
|
import org.springframework.validation.ObjectError;
|
||||||
|
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||||
|
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||||
|
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||||
|
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||||
|
|
||||||
|
@ControllerAdvice
|
||||||
|
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
|
||||||
|
|
||||||
|
// 400
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResponseEntity<Object> handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||||
|
logger.info(ex.getClass().getName());
|
||||||
|
//
|
||||||
|
final List<String> errors = new ArrayList<String>();
|
||||||
|
for (final FieldError error : ex.getBindingResult().getFieldErrors()) {
|
||||||
|
errors.add(error.getField() + ": " + error.getDefaultMessage());
|
||||||
|
}
|
||||||
|
for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
|
||||||
|
errors.add(error.getObjectName() + ": " + error.getDefaultMessage());
|
||||||
|
}
|
||||||
|
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
|
||||||
|
return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResponseEntity<Object> handleBindException(final BindException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||||
|
logger.info(ex.getClass().getName());
|
||||||
|
//
|
||||||
|
final List<String> errors = new ArrayList<String>();
|
||||||
|
for (final FieldError error : ex.getBindingResult().getFieldErrors()) {
|
||||||
|
errors.add(error.getField() + ": " + error.getDefaultMessage());
|
||||||
|
}
|
||||||
|
for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
|
||||||
|
errors.add(error.getObjectName() + ": " + error.getDefaultMessage());
|
||||||
|
}
|
||||||
|
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
|
||||||
|
return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResponseEntity<Object> handleTypeMismatch(final TypeMismatchException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||||
|
logger.info(ex.getClass().getName());
|
||||||
|
//
|
||||||
|
final String error = ex.getValue() + " value for " + ex.getPropertyName() + " should be of type " + ex.getRequiredType();
|
||||||
|
|
||||||
|
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
|
||||||
|
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler({ MethodArgumentTypeMismatchException.class })
|
||||||
|
public ResponseEntity<Object> handleMethodArgumentTypeMismatch(final MethodArgumentTypeMismatchException ex, final WebRequest request) {
|
||||||
|
logger.info(ex.getClass().getName());
|
||||||
|
//
|
||||||
|
final String error = ex.getName() + " should be of type " + ex.getRequiredType().getName();
|
||||||
|
|
||||||
|
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
|
||||||
|
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResponseEntity<Object> handleMissingServletRequestPart(final MissingServletRequestPartException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||||
|
logger.info(ex.getClass().getName());
|
||||||
|
//
|
||||||
|
final String error = ex.getRequestPartName() + " part is missing";
|
||||||
|
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
|
||||||
|
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResponseEntity<Object> handleMissingServletRequestParameter(final MissingServletRequestParameterException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||||
|
logger.info(ex.getClass().getName());
|
||||||
|
//
|
||||||
|
final String error = ex.getParameterName() + " parameter is missing";
|
||||||
|
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
|
||||||
|
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 404
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResponseEntity<Object> handleNoHandlerFoundException(final NoHandlerFoundException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||||
|
logger.info(ex.getClass().getName());
|
||||||
|
//
|
||||||
|
final String error = "No handler found for " + ex.getHttpMethod() + " " + ex.getRequestURL();
|
||||||
|
|
||||||
|
final ApiError apiError = new ApiError(HttpStatus.NOT_FOUND, ex.getLocalizedMessage(), error);
|
||||||
|
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 405
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(final HttpRequestMethodNotSupportedException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||||
|
logger.info(ex.getClass().getName());
|
||||||
|
//
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(ex.getMethod());
|
||||||
|
builder.append(" method is not supported for this request. Supported methods are ");
|
||||||
|
ex.getSupportedMethods();
|
||||||
|
|
||||||
|
final ApiError apiError = new ApiError(HttpStatus.METHOD_NOT_ALLOWED, ex.getLocalizedMessage(), builder.toString());
|
||||||
|
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 415
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(final HttpMediaTypeNotSupportedException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||||
|
logger.info(ex.getClass().getName());
|
||||||
|
//
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(ex.getContentType());
|
||||||
|
builder.append(" media type is not supported. Supported media types are ");
|
||||||
|
ex.getSupportedMediaTypes().forEach(t -> builder.append(t + ", "));
|
||||||
|
|
||||||
|
final ApiError apiError = new ApiError(HttpStatus.UNSUPPORTED_MEDIA_TYPE, ex.getLocalizedMessage(), builder.substring(0, builder.length() - 2));
|
||||||
|
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 500
|
||||||
|
|
||||||
|
@ExceptionHandler({ Exception.class })
|
||||||
|
public ResponseEntity<Object> handleAll(final Exception ex, final WebRequest request) {
|
||||||
|
logger.info(ex.getClass().getName());
|
||||||
|
//
|
||||||
|
final ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage(), "error occurred");
|
||||||
|
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -9,11 +9,14 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
import org.baeldung.persistence.model.Foo;
|
import org.baeldung.persistence.model.Foo;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
@ -47,4 +50,11 @@ public class FooController {
|
||||||
return Lists.newArrayList(new Foo(randomAlphabetic(6)));
|
return Lists.newArrayList(new Foo(randomAlphabetic(6)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write - just for test
|
||||||
|
@RequestMapping(method = RequestMethod.POST)
|
||||||
|
@ResponseStatus(HttpStatus.CREATED)
|
||||||
|
@ResponseBody
|
||||||
|
public Foo create(@RequestBody final Foo foo) {
|
||||||
|
return foo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,10 @@
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>api</servlet-name>
|
<servlet-name>api</servlet-name>
|
||||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||||
<load-on-startup>1</load-on-startup>
|
<init-param>
|
||||||
|
<param-name>throwExceptionIfNoHandlerFound</param-name>
|
||||||
|
<param-value>true</param-value>
|
||||||
|
</init-param>
|
||||||
</servlet>
|
</servlet>
|
||||||
<servlet-mapping>
|
<servlet-mapping>
|
||||||
<servlet-name>api</servlet-name>
|
<servlet-name>api</servlet-name>
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
package org.baeldung.web;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||||
|
|
||||||
|
import com.jayway.restassured.RestAssured;
|
||||||
|
import com.jayway.restassured.authentication.FormAuthConfig;
|
||||||
|
import com.jayway.restassured.response.Response;
|
||||||
|
import com.jayway.restassured.specification.RequestSpecification;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration(classes = { TestConfig.class }, loader = AnnotationConfigContextLoader.class)
|
||||||
|
public class FooLiveTest {
|
||||||
|
private static final String URL_PREFIX = "http://localhost:8080/spring-security-rest";
|
||||||
|
private FormAuthConfig formConfig = new FormAuthConfig(URL_PREFIX + "/login", "username", "password");
|
||||||
|
|
||||||
|
private RequestSpecification givenAuth() {
|
||||||
|
return RestAssured.given().auth().form("user", "userPass", formConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenMethodArgumentMismatch_thenBadRequest() {
|
||||||
|
final Response response = givenAuth().get(URL_PREFIX + "/api/foos/ccc");
|
||||||
|
final ApiError error = response.as(ApiError.class);
|
||||||
|
assertEquals(HttpStatus.BAD_REQUEST, error.getStatus());
|
||||||
|
assertEquals(1, error.getErrors().size());
|
||||||
|
assertTrue(error.getErrors().get(0).contains("should be of type"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenNoHandlerForHttpRequest_thenNotFound() {
|
||||||
|
final Response response = givenAuth().delete(URL_PREFIX + "/api/xx");
|
||||||
|
final ApiError error = response.as(ApiError.class);
|
||||||
|
assertEquals(HttpStatus.NOT_FOUND, error.getStatus());
|
||||||
|
assertEquals(1, error.getErrors().size());
|
||||||
|
assertTrue(error.getErrors().get(0).contains("No handler found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenHttpRequestMethodNotSupported_thenMethodNotAllowed() {
|
||||||
|
final Response response = givenAuth().delete(URL_PREFIX + "/api/foos/1");
|
||||||
|
final ApiError error = response.as(ApiError.class);
|
||||||
|
assertEquals(HttpStatus.METHOD_NOT_ALLOWED, error.getStatus());
|
||||||
|
assertEquals(1, error.getErrors().size());
|
||||||
|
assertTrue(error.getErrors().get(0).contains("Supported methods are"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenSendInvalidHttpMediaType_thenUnsupportedMediaType() {
|
||||||
|
final Response response = givenAuth().body("").post(URL_PREFIX + "/api/foos");
|
||||||
|
final ApiError error = response.as(ApiError.class);
|
||||||
|
assertEquals(HttpStatus.UNSUPPORTED_MEDIA_TYPE, error.getStatus());
|
||||||
|
assertEquals(1, error.getErrors().size());
|
||||||
|
assertTrue(error.getErrors().get(0).contains("media type is not supported"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.baeldung.web;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan({ "org.baeldung.web" })
|
||||||
|
public class TestConfig {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue