Skip to content

Commit

Permalink
Use MessageSource for @ExceptionHandler methods
Browse files Browse the repository at this point in the history
  • Loading branch information
quaff committed Jul 26, 2021
1 parent 9c08256 commit b350594
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 3 deletions.
Expand Up @@ -102,11 +102,20 @@ public class HandlerMethod {
* Create an instance from a bean instance and a method.
*/
public HandlerMethod(Object bean, Method method) {
this(bean, method, null);
}


/**
* Variant of {@link #HandlerMethod(Object, Method)} that
* also accepts a {@link MessageSource}.
*/
public HandlerMethod(Object bean, Method method, @Nullable MessageSource messageSource) {
Assert.notNull(bean, "Bean is required");
Assert.notNull(method, "Method is required");
this.bean = bean;
this.beanFactory = null;
this.messageSource = null;
this.messageSource = messageSource;
this.beanType = ClassUtils.getUserClass(bean);
this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
Expand Down
Expand Up @@ -20,6 +20,7 @@
import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.context.MessageSource;
import org.springframework.core.CoroutinesUtils;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.KotlinDetector;
Expand Down Expand Up @@ -71,6 +72,14 @@ public InvocableHandlerMethod(Object bean, Method method) {
super(bean, method);
}

/**
* Variant of {@link #InvocableHandlerMethod(Object, Method)} that
* also accepts a {@link MessageSource}.
*/
public InvocableHandlerMethod(Object bean, Method method, @Nullable MessageSource messageSource) {
super(bean, method, messageSource);
}

/**
* Construct a new handler method with the given bean instance, method name and parameters.
* @param bean the object bean
Expand Down
Expand Up @@ -483,7 +483,7 @@ protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method, this.applicationContext);
}
// For advice applicability check below (involving base packages, assignable types
// and annotation presence), use target class instead of interface-based proxy.
Expand All @@ -498,7 +498,7 @@ protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
return new ServletInvocableHandlerMethod(advice.resolveBean(), method, this.applicationContext);
}
}
}
Expand Down
Expand Up @@ -25,6 +25,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.MessageSource;
import org.springframework.core.KotlinDetector;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
Expand Down Expand Up @@ -68,6 +69,13 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;

/**
* Variant of {@link #ServletInvocableHandlerMethod(Object, Method)} that
* also accepts a {@link MessageSource}.
*/
public ServletInvocableHandlerMethod(Object handler, Method method, @Nullable MessageSource messageSource) {
super(handler, method, messageSource);
}

/**
* Creates an instance from the given handler and method.
Expand Down
Expand Up @@ -19,7 +19,9 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.SocketTimeoutException;
import java.util.Collections;
import java.util.Locale;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -30,8 +32,11 @@
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
Expand All @@ -42,6 +47,7 @@
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.support.WebApplicationObjectSupport;
import org.springframework.web.method.HandlerMethod;
Expand Down Expand Up @@ -333,6 +339,28 @@ void resolveExceptionWithAssertionErrorAsRootCause() throws Exception {
assertThat(this.response.getContentAsString()).isEqualTo(rootCause.toString());
}

@Test //gh-27156
void resolveExceptionWithReasonResovledByMessageSource() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
StaticApplicationContext context = new StaticApplicationContext(ctx);
Locale locale = Locale.ENGLISH;
context.addMessage("gateway.timeout", locale, "Gateway Timeout");
context.refresh();
LocaleContextHolder.setLocale(locale);
this.resolver.setApplicationContext(context);
this.resolver.afterPropertiesSet();

SocketTimeoutException ex = new SocketTimeoutException();
HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);

assertThat(mav).as("Exception was not handled").isNotNull();
assertThat(mav.isEmpty()).isTrue();
assertThat(this.response.getStatus()).isEqualTo(HttpStatus.GATEWAY_TIMEOUT.value());
assertThat(this.response.getErrorMessage()).isEqualTo("Gateway Timeout");
assertThat(this.response.getContentAsString()).isEqualTo("");
}

@Test
void resolveExceptionControllerAdviceHandler() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class);
Expand Down Expand Up @@ -530,6 +558,16 @@ public String handleException(Exception ex) {
}
}

@RestControllerAdvice
@Order(3)
static class ResponseStatusTestExceptionResolver {

@ExceptionHandler(SocketTimeoutException.class)
@ResponseStatus(code = HttpStatus.GATEWAY_TIMEOUT, reason = "gateway.timeout")
public void handleException(SocketTimeoutException ex) {

}
}

@Configuration
static class MyConfig {
Expand All @@ -543,6 +581,11 @@ public TestExceptionResolver testExceptionResolver() {
public AnotherTestExceptionResolver anotherTestExceptionResolver() {
return new AnotherTestExceptionResolver();
}

@Bean
public ResponseStatusTestExceptionResolver responseStatusTestExceptionResolver() {
return new ResponseStatusTestExceptionResolver();
}
}


Expand Down

0 comments on commit b350594

Please sign in to comment.