Skip to content

Commit b350594

Browse files
committed
Use MessageSource for @ExceptionHandler methods
Continue bb816c1 Fix gh-27156
1 parent 9c08256 commit b350594

File tree

5 files changed

+72
-3
lines changed

5 files changed

+72
-3
lines changed

spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,20 @@ public class HandlerMethod {
102102
* Create an instance from a bean instance and a method.
103103
*/
104104
public HandlerMethod(Object bean, Method method) {
105+
this(bean, method, null);
106+
}
107+
108+
109+
/**
110+
* Variant of {@link #HandlerMethod(Object, Method)} that
111+
* also accepts a {@link MessageSource}.
112+
*/
113+
public HandlerMethod(Object bean, Method method, @Nullable MessageSource messageSource) {
105114
Assert.notNull(bean, "Bean is required");
106115
Assert.notNull(method, "Method is required");
107116
this.bean = bean;
108117
this.beanFactory = null;
109-
this.messageSource = null;
118+
this.messageSource = messageSource;
110119
this.beanType = ClassUtils.getUserClass(bean);
111120
this.method = method;
112121
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);

spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.lang.reflect.Method;
2121
import java.util.Arrays;
2222

23+
import org.springframework.context.MessageSource;
2324
import org.springframework.core.CoroutinesUtils;
2425
import org.springframework.core.DefaultParameterNameDiscoverer;
2526
import org.springframework.core.KotlinDetector;
@@ -71,6 +72,14 @@ public InvocableHandlerMethod(Object bean, Method method) {
7172
super(bean, method);
7273
}
7374

75+
/**
76+
* Variant of {@link #InvocableHandlerMethod(Object, Method)} that
77+
* also accepts a {@link MessageSource}.
78+
*/
79+
public InvocableHandlerMethod(Object bean, Method method, @Nullable MessageSource messageSource) {
80+
super(bean, method, messageSource);
81+
}
82+
7483
/**
7584
* Construct a new handler method with the given bean instance, method name and parameters.
7685
* @param bean the object bean

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
483483
}
484484
Method method = resolver.resolveMethod(exception);
485485
if (method != null) {
486-
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
486+
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method, this.applicationContext);
487487
}
488488
// For advice applicability check below (involving base packages, assignable types
489489
// and annotation presence), use target class instead of interface-based proxy.
@@ -498,7 +498,7 @@ protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
498498
ExceptionHandlerMethodResolver resolver = entry.getValue();
499499
Method method = resolver.resolveMethod(exception);
500500
if (method != null) {
501-
return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
501+
return new ServletInvocableHandlerMethod(advice.resolveBean(), method, this.applicationContext);
502502
}
503503
}
504504
}

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import javax.servlet.http.HttpServletRequest;
2626
import javax.servlet.http.HttpServletResponse;
2727

28+
import org.springframework.context.MessageSource;
2829
import org.springframework.core.KotlinDetector;
2930
import org.springframework.core.MethodParameter;
3031
import org.springframework.core.ResolvableType;
@@ -68,6 +69,13 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
6869
@Nullable
6970
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
7071

72+
/**
73+
* Variant of {@link #ServletInvocableHandlerMethod(Object, Method)} that
74+
* also accepts a {@link MessageSource}.
75+
*/
76+
public ServletInvocableHandlerMethod(Object handler, Method method, @Nullable MessageSource messageSource) {
77+
super(handler, method, messageSource);
78+
}
7179

7280
/**
7381
* Creates an instance from the given handler and method.

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import java.io.IOException;
2020
import java.io.UnsupportedEncodingException;
2121
import java.io.Writer;
22+
import java.net.SocketTimeoutException;
2223
import java.util.Collections;
24+
import java.util.Locale;
2325

2426
import org.junit.jupiter.api.BeforeAll;
2527
import org.junit.jupiter.api.BeforeEach;
@@ -30,8 +32,11 @@
3032
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3133
import org.springframework.context.annotation.Bean;
3234
import org.springframework.context.annotation.Configuration;
35+
import org.springframework.context.i18n.LocaleContextHolder;
36+
import org.springframework.context.support.StaticApplicationContext;
3337
import org.springframework.core.MethodParameter;
3438
import org.springframework.core.annotation.Order;
39+
import org.springframework.http.HttpStatus;
3540
import org.springframework.http.MediaType;
3641
import org.springframework.http.converter.HttpMessageConverter;
3742
import org.springframework.http.server.ServerHttpRequest;
@@ -42,6 +47,7 @@
4247
import org.springframework.web.HttpRequestHandler;
4348
import org.springframework.web.bind.annotation.ExceptionHandler;
4449
import org.springframework.web.bind.annotation.ResponseBody;
50+
import org.springframework.web.bind.annotation.ResponseStatus;
4551
import org.springframework.web.bind.annotation.RestControllerAdvice;
4652
import org.springframework.web.context.support.WebApplicationObjectSupport;
4753
import org.springframework.web.method.HandlerMethod;
@@ -333,6 +339,28 @@ void resolveExceptionWithAssertionErrorAsRootCause() throws Exception {
333339
assertThat(this.response.getContentAsString()).isEqualTo(rootCause.toString());
334340
}
335341

342+
@Test //gh-27156
343+
void resolveExceptionWithReasonResovledByMessageSource() throws Exception {
344+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
345+
StaticApplicationContext context = new StaticApplicationContext(ctx);
346+
Locale locale = Locale.ENGLISH;
347+
context.addMessage("gateway.timeout", locale, "Gateway Timeout");
348+
context.refresh();
349+
LocaleContextHolder.setLocale(locale);
350+
this.resolver.setApplicationContext(context);
351+
this.resolver.afterPropertiesSet();
352+
353+
SocketTimeoutException ex = new SocketTimeoutException();
354+
HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
355+
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
356+
357+
assertThat(mav).as("Exception was not handled").isNotNull();
358+
assertThat(mav.isEmpty()).isTrue();
359+
assertThat(this.response.getStatus()).isEqualTo(HttpStatus.GATEWAY_TIMEOUT.value());
360+
assertThat(this.response.getErrorMessage()).isEqualTo("Gateway Timeout");
361+
assertThat(this.response.getContentAsString()).isEqualTo("");
362+
}
363+
336364
@Test
337365
void resolveExceptionControllerAdviceHandler() throws Exception {
338366
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class);
@@ -530,6 +558,16 @@ public String handleException(Exception ex) {
530558
}
531559
}
532560

561+
@RestControllerAdvice
562+
@Order(3)
563+
static class ResponseStatusTestExceptionResolver {
564+
565+
@ExceptionHandler(SocketTimeoutException.class)
566+
@ResponseStatus(code = HttpStatus.GATEWAY_TIMEOUT, reason = "gateway.timeout")
567+
public void handleException(SocketTimeoutException ex) {
568+
569+
}
570+
}
533571

534572
@Configuration
535573
static class MyConfig {
@@ -543,6 +581,11 @@ public TestExceptionResolver testExceptionResolver() {
543581
public AnotherTestExceptionResolver anotherTestExceptionResolver() {
544582
return new AnotherTestExceptionResolver();
545583
}
584+
585+
@Bean
586+
public ResponseStatusTestExceptionResolver responseStatusTestExceptionResolver() {
587+
return new ResponseStatusTestExceptionResolver();
588+
}
546589
}
547590

548591

0 commit comments

Comments
 (0)