Skip to content

Commit bb816c1

Browse files
committed
Use MessageSource in HandlerMethod for error reason
Closes gh-27156
1 parent 33f3aa9 commit bb816c1

File tree

4 files changed

+78
-4
lines changed

4 files changed

+78
-4
lines changed

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

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,8 @@
2929
import org.apache.commons.logging.LogFactory;
3030

3131
import org.springframework.beans.factory.BeanFactory;
32+
import org.springframework.context.MessageSource;
33+
import org.springframework.context.i18n.LocaleContextHolder;
3234
import org.springframework.core.BridgeMethodResolver;
3335
import org.springframework.core.MethodParameter;
3436
import org.springframework.core.ResolvableType;
@@ -70,6 +72,9 @@ public class HandlerMethod {
7072
@Nullable
7173
private final BeanFactory beanFactory;
7274

75+
@Nullable
76+
private final MessageSource messageSource;
77+
7378
private final Class<?> beanType;
7479

7580
private final Method method;
@@ -101,6 +106,7 @@ public HandlerMethod(Object bean, Method method) {
101106
Assert.notNull(method, "Method is required");
102107
this.bean = bean;
103108
this.beanFactory = null;
109+
this.messageSource = null;
104110
this.beanType = ClassUtils.getUserClass(bean);
105111
this.method = method;
106112
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
@@ -118,6 +124,7 @@ public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
118124
Assert.notNull(methodName, "Method name is required");
119125
this.bean = bean;
120126
this.beanFactory = null;
127+
this.messageSource = null;
121128
this.beanType = ClassUtils.getUserClass(bean);
122129
this.method = bean.getClass().getMethod(methodName, parameterTypes);
123130
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method);
@@ -132,11 +139,23 @@ public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
132139
* re-create the {@code HandlerMethod} with an initialized bean.
133140
*/
134141
public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
142+
this(beanName, beanFactory, null, method);
143+
}
144+
145+
/**
146+
* Variant of {@link #HandlerMethod(String, BeanFactory, Method)} that
147+
* also accepts a {@link MessageSource}.
148+
*/
149+
public HandlerMethod(
150+
String beanName, BeanFactory beanFactory,
151+
@Nullable MessageSource messageSource, Method method) {
152+
135153
Assert.hasText(beanName, "Bean name is required");
136154
Assert.notNull(beanFactory, "BeanFactory is required");
137155
Assert.notNull(method, "Method is required");
138156
this.bean = beanName;
139157
this.beanFactory = beanFactory;
158+
this.messageSource = messageSource;
140159
Class<?> beanType = beanFactory.getType(beanName);
141160
if (beanType == null) {
142161
throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
@@ -156,6 +175,7 @@ protected HandlerMethod(HandlerMethod handlerMethod) {
156175
Assert.notNull(handlerMethod, "HandlerMethod is required");
157176
this.bean = handlerMethod.bean;
158177
this.beanFactory = handlerMethod.beanFactory;
178+
this.messageSource = handlerMethod.messageSource;
159179
this.beanType = handlerMethod.beanType;
160180
this.method = handlerMethod.method;
161181
this.bridgedMethod = handlerMethod.bridgedMethod;
@@ -174,6 +194,7 @@ private HandlerMethod(HandlerMethod handlerMethod, Object handler) {
174194
Assert.notNull(handler, "Handler object is required");
175195
this.bean = handler;
176196
this.beanFactory = handlerMethod.beanFactory;
197+
this.messageSource = handlerMethod.messageSource;
177198
this.beanType = handlerMethod.beanType;
178199
this.method = handlerMethod.method;
179200
this.bridgedMethod = handlerMethod.bridgedMethod;
@@ -199,8 +220,13 @@ private void evaluateResponseStatus() {
199220
annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
200221
}
201222
if (annotation != null) {
223+
String reason = annotation.reason();
224+
String resolvedReason = (StringUtils.hasText(reason) && this.messageSource != null ?
225+
this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
226+
reason);
227+
202228
this.responseStatus = annotation.code();
203-
this.responseStatusReason = annotation.reason();
229+
this.responseStatusReason = resolvedReason;
204230
}
205231
}
206232

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,9 @@ protected void registerHandlerMethod(Object handler, Method method, T mapping) {
258258
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
259259
if (handler instanceof String) {
260260
return new HandlerMethod((String) handler,
261-
obtainApplicationContext().getAutowireCapableBeanFactory(), method);
261+
obtainApplicationContext().getAutowireCapableBeanFactory(),
262+
obtainApplicationContext(),
263+
method);
262264
}
263265
return new HandlerMethod(handler, method);
264266
}

spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,9 @@ protected void registerHandlerMethod(Object handler, Method method, T mapping) {
337337
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
338338
if (handler instanceof String) {
339339
return new HandlerMethod((String) handler,
340-
obtainApplicationContext().getAutowireCapableBeanFactory(), method);
340+
obtainApplicationContext().getAutowireCapableBeanFactory(),
341+
obtainApplicationContext(),
342+
method);
341343
}
342344
return new HandlerMethod(handler, method);
343345
}

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@
2222
import java.util.Arrays;
2323
import java.util.Collections;
2424
import java.util.List;
25+
import java.util.Locale;
2526

2627
import javax.servlet.FilterChain;
2728
import javax.servlet.http.HttpServletResponse;
2829

2930
import org.junit.jupiter.api.Test;
3031
import reactor.core.publisher.Flux;
3132

33+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
34+
import org.springframework.context.i18n.LocaleContextHolder;
35+
import org.springframework.context.support.StaticApplicationContext;
3236
import org.springframework.core.MethodParameter;
3337
import org.springframework.core.ResolvableType;
3438
import org.springframework.core.annotation.AliasFor;
@@ -47,12 +51,14 @@
4751
import org.springframework.web.context.request.ServletWebRequest;
4852
import org.springframework.web.context.request.async.DeferredResult;
4953
import org.springframework.web.filter.ShallowEtagHeaderFilter;
54+
import org.springframework.web.method.HandlerMethod;
5055
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
5156
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
5257
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
5358
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
5459
import org.springframework.web.method.support.ModelAndViewContainer;
5560
import org.springframework.web.servlet.view.RedirectView;
61+
import org.springframework.web.testfixture.method.ResolvableMethod;
5662
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
5763
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
5864

@@ -180,6 +186,39 @@ public void invokeAndHandle_NotVoidWithResponseStatusAndReason() throws Exceptio
180186
.as("When a status reason w/ used, the request is handled").isTrue();
181187
}
182188

189+
@Test
190+
public void invokeAndHandle_responseStatusAndReasonCode() throws Exception {
191+
Locale locale = Locale.ENGLISH;
192+
193+
String beanName = "handler";
194+
StaticApplicationContext context = new StaticApplicationContext();
195+
context.registerBean(beanName, Handler.class);
196+
context.addMessage("BadRequest.error", locale, "Bad request message");
197+
context.refresh();
198+
199+
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
200+
System.out.println(beanFactory.getType(beanName));
201+
202+
LocaleContextHolder.setLocale(locale);
203+
try {
204+
Method method = ResolvableMethod.on(Handler.class)
205+
.named("responseStatusWithReasonCode")
206+
.resolveMethod();
207+
208+
HandlerMethod handlerMethod = new HandlerMethod(beanName, beanFactory, context, method);
209+
handlerMethod = handlerMethod.createWithResolvedBean();
210+
211+
new ServletInvocableHandlerMethod(handlerMethod)
212+
.invokeAndHandle(this.webRequest, this.mavContainer);
213+
}
214+
finally {
215+
LocaleContextHolder.resetLocaleContext();
216+
}
217+
218+
assertThat(this.response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
219+
assertThat(this.response.getErrorMessage()).isEqualTo("Bad request message");
220+
}
221+
183222
@Test // gh-23775, gh-24635
184223
public void invokeAndHandle_ETagFilterHasNoImpactWhenETagPresent() throws Exception {
185224

@@ -417,6 +456,11 @@ public String responseStatusWithReason() {
417456
return "foo";
418457
}
419458

459+
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "BadRequest.error")
460+
public String responseStatusWithReasonCode() {
461+
return "foo";
462+
}
463+
420464
@ComposedResponseStatus(responseStatus = HttpStatus.BAD_REQUEST)
421465
public void composedResponseStatus() {
422466
}

0 commit comments

Comments
 (0)