DefaultHandlerExceptionResolver supports ErrorResponse
DefaultHandlerExceptionResolver now supports ErrorResponse exceptions and can map them to HTTP status and headers of the response. This includes not only exceptions from spring-web, but also any other exception that implements ErrorResponse. ResponseEntityExceptionHandler is updated along the same lines, now also handling any ErrorResponseException. It can be used it for RFC 7807 support for Spring MVC's own exceptions. See gh-27052
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -37,6 +38,7 @@ import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.MapBindingResult;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
@@ -60,7 +62,6 @@ import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
|
||||
import org.springframework.web.testfixture.servlet.MockServletConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link ResponseEntityExceptionHandler}.
|
||||
@@ -80,20 +81,21 @@ public class ResponseEntityExceptionHandlerTests {
|
||||
private WebRequest request = new ServletWebRequest(this.servletRequest, this.servletResponse);
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void supportsAllDefaultHandlerExceptionResolverExceptionTypes() throws Exception {
|
||||
Class<ResponseEntityExceptionHandler> clazz = ResponseEntityExceptionHandler.class;
|
||||
Method handleExceptionMethod = clazz.getMethod("handleException", Exception.class, WebRequest.class);
|
||||
ExceptionHandler annotation = handleExceptionMethod.getAnnotation(ExceptionHandler.class);
|
||||
List<Class<?>> exceptionTypes = Arrays.asList(annotation.value());
|
||||
|
||||
for (Method method : DefaultHandlerExceptionResolver.class.getDeclaredMethods()) {
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
if (method.getName().startsWith("handle") && (paramTypes.length == 4)) {
|
||||
String name = paramTypes[0].getSimpleName();
|
||||
assertThat(exceptionTypes.contains(paramTypes[0])).as("@ExceptionHandler is missing " + name).isTrue();
|
||||
}
|
||||
}
|
||||
ExceptionHandler annotation = ResponseEntityExceptionHandler.class
|
||||
.getMethod("handleException", Exception.class, WebRequest.class)
|
||||
.getAnnotation(ExceptionHandler.class);
|
||||
|
||||
Arrays.stream(DefaultHandlerExceptionResolver.class.getDeclaredMethods())
|
||||
.filter(method -> method.getName().startsWith("handle") && (method.getParameterCount() == 4))
|
||||
.filter(method -> !method.getName().equals("handleErrorResponse"))
|
||||
.map(method -> method.getParameterTypes()[0])
|
||||
.forEach(exceptionType -> assertThat(annotation.value())
|
||||
.as("@ExceptionHandler is missing declaration for " + exceptionType.getName())
|
||||
.contains((Class<Exception>) exceptionType));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -121,7 +123,7 @@ public class ResponseEntityExceptionHandlerTests {
|
||||
this.request = new ServletWebRequest(this.servletRequest, this.servletResponse);
|
||||
|
||||
List<MediaType> acceptable = Arrays.asList(MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_XML);
|
||||
Exception ex = new HttpMediaTypeNotSupportedException(MediaType.APPLICATION_JSON, acceptable);
|
||||
Exception ex = new HttpMediaTypeNotSupportedException(MediaType.APPLICATION_JSON, acceptable, HttpMethod.PATCH);
|
||||
|
||||
ResponseEntity<Object> responseEntity = testException(ex);
|
||||
assertThat(responseEntity.getHeaders().getAccept()).isEqualTo(acceptable);
|
||||
@@ -180,8 +182,10 @@ public class ResponseEntityExceptionHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodArgumentNotValid() {
|
||||
Exception ex = mock(MethodArgumentNotValidException.class);
|
||||
public void methodArgumentNotValid() throws Exception {
|
||||
Exception ex = new MethodArgumentNotValidException(
|
||||
new MethodParameter(getClass().getDeclaredMethod("handle", String.class), 0),
|
||||
new MapBindingResult(Collections.emptyMap(), "name"));
|
||||
testException(ex);
|
||||
}
|
||||
|
||||
@@ -328,6 +332,7 @@ public class ResponseEntityExceptionHandlerTests {
|
||||
protected ResponseEntity<Object> handleServletRequestBindingException(
|
||||
ServletRequestBindingException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
|
||||
|
||||
headers = new HttpHeaders();
|
||||
headers.set("someHeader", "someHeaderValue");
|
||||
return handleExceptionInternal(ex, "error content", headers, status, request);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -26,6 +26,7 @@ import org.springframework.beans.ConversionNotSupportedException;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
@@ -48,6 +49,8 @@ import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultHandlerExceptionResolver}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class DefaultHandlerExceptionResolverTests {
|
||||
@@ -89,8 +92,10 @@ public class DefaultHandlerExceptionResolverTests {
|
||||
|
||||
@Test
|
||||
public void patchHttpMediaTypeNotSupported() {
|
||||
HttpMediaTypeNotSupportedException ex = new HttpMediaTypeNotSupportedException(new MediaType("text", "plain"),
|
||||
Collections.singletonList(new MediaType("application", "pdf")));
|
||||
HttpMediaTypeNotSupportedException ex = new HttpMediaTypeNotSupportedException(
|
||||
new MediaType("text", "plain"),
|
||||
Collections.singletonList(new MediaType("application", "pdf")),
|
||||
HttpMethod.PATCH);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("PATCH", "/");
|
||||
ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex);
|
||||
assertThat(mav).as("No ModelAndView returned").isNotNull();
|
||||
@@ -108,8 +113,7 @@ public class DefaultHandlerExceptionResolverTests {
|
||||
assertThat(mav).as("No ModelAndView returned").isNotNull();
|
||||
assertThat(mav.isEmpty()).as("No Empty ModelAndView returned").isTrue();
|
||||
assertThat(response.getStatus()).as("Invalid status code").isEqualTo(500);
|
||||
assertThat(response.getErrorMessage())
|
||||
.isEqualTo("Required URI template variable 'foo' for method parameter type String is not present");
|
||||
assertThat(response.getErrorMessage()).isEqualTo("Required URI variable 'foo' is not present");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -119,8 +123,7 @@ public class DefaultHandlerExceptionResolverTests {
|
||||
assertThat(mav).as("No ModelAndView returned").isNotNull();
|
||||
assertThat(mav.isEmpty()).as("No Empty ModelAndView returned").isTrue();
|
||||
assertThat(response.getStatus()).as("Invalid status code").isEqualTo(400);
|
||||
assertThat(response.getErrorMessage()).isEqualTo(
|
||||
"Required request parameter 'foo' for method parameter type bar is not present");
|
||||
assertThat(response.getErrorMessage()).isEqualTo("Required parameter 'foo' is not present");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user