MessageSource support for Spring MVC and WebFlux exceptions
See gh-28814
This commit is contained in:
@@ -16,12 +16,17 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.ConversionNotSupportedException;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
@@ -64,7 +69,7 @@ import org.springframework.web.util.WebUtils;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.2
|
||||
*/
|
||||
public abstract class ResponseEntityExceptionHandler {
|
||||
public abstract class ResponseEntityExceptionHandler implements MessageSourceAware {
|
||||
|
||||
/**
|
||||
* Log category to use when no mapped handler is found for a request.
|
||||
@@ -84,6 +89,16 @@ public abstract class ResponseEntityExceptionHandler {
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
||||
@Nullable
|
||||
private MessageSource messageSource;
|
||||
|
||||
|
||||
@Override
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle all exceptions raised within Spring MVC handling of the request .
|
||||
* @param ex the exception to handle
|
||||
@@ -504,12 +519,25 @@ public abstract class ResponseEntityExceptionHandler {
|
||||
}
|
||||
|
||||
if (body == null && ex instanceof ErrorResponse errorResponse) {
|
||||
body = errorResponse.getBody();
|
||||
body = resolveDetailViaMessageSource(errorResponse);
|
||||
}
|
||||
|
||||
return createResponseEntity(body, headers, statusCode, request);
|
||||
}
|
||||
|
||||
private ProblemDetail resolveDetailViaMessageSource(ErrorResponse response) {
|
||||
ProblemDetail body = response.getBody();
|
||||
if (this.messageSource != null) {
|
||||
Locale locale = LocaleContextHolder.getLocale();
|
||||
Object[] arguments = response.getDetailMessageArguments(this.messageSource, locale);
|
||||
String detail = this.messageSource.getMessage(response.getDetailMessageCode(), arguments, null, locale);
|
||||
if (detail != null) {
|
||||
body.setDetail(detail);
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@link ResponseEntity} to use from the given body, headers,
|
||||
* and statusCode. Subclasses can override this method to inspect and possibly
|
||||
|
||||
@@ -19,18 +19,22 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.ConversionNotSupportedException;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ProblemDetail;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
@@ -106,7 +110,7 @@ public class ResponseEntityExceptionHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleHttpMediaTypeNotSupported() {
|
||||
public void httpMediaTypeNotSupported() {
|
||||
ResponseEntity<Object> entity = testException(new HttpMediaTypeNotSupportedException(
|
||||
MediaType.APPLICATION_JSON, List.of(MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_XML)));
|
||||
|
||||
@@ -152,6 +156,32 @@ public class ResponseEntityExceptionHandlerTests {
|
||||
testException(new ServletRequestBindingException("message"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void errorResponseProblemDetailViaMessageSource() {
|
||||
|
||||
Locale locale = Locale.UK;
|
||||
LocaleContextHolder.setLocale(locale);
|
||||
|
||||
try {
|
||||
StaticMessageSource messageSource = new StaticMessageSource();
|
||||
messageSource.addMessage(
|
||||
"problemDetail." + HttpMediaTypeNotSupportedException.class.getName(), locale,
|
||||
"Content-Type {0} not supported. Supported: {1}");
|
||||
|
||||
this.exceptionHandler.setMessageSource(messageSource);
|
||||
|
||||
ResponseEntity<?> entity = testException(new HttpMediaTypeNotSupportedException(
|
||||
MediaType.APPLICATION_JSON, List.of(MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_XML)));
|
||||
|
||||
ProblemDetail body = (ProblemDetail) entity.getBody();
|
||||
assertThat(body.getDetail()).isEqualTo(
|
||||
"Content-Type application/json not supported. Supported: [application/atom+xml, application/xml]");
|
||||
}
|
||||
finally {
|
||||
LocaleContextHolder.resetLocaleContext();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void conversionNotSupported() {
|
||||
testException(new ConversionNotSupportedException(new Object(), Object.class, null));
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.web.servlet.mvc.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -71,7 +72,7 @@ public class DefaultHandlerExceptionResolverTests {
|
||||
@Test
|
||||
public void handleHttpRequestMethodNotSupported() {
|
||||
HttpRequestMethodNotSupportedException ex =
|
||||
new HttpRequestMethodNotSupportedException("GET", new String[]{"POST", "PUT"});
|
||||
new HttpRequestMethodNotSupportedException("GET", Arrays.asList("POST", "PUT"));
|
||||
ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex);
|
||||
assertThat(mav).as("No ModelAndView returned").isNotNull();
|
||||
assertThat(mav.isEmpty()).as("No Empty ModelAndView returned").isTrue();
|
||||
|
||||
Reference in New Issue
Block a user