Skip to content

Commit

Permalink
Use MessageSource in HandlerMethod for error reason
Browse files Browse the repository at this point in the history
Closes gh-27156
  • Loading branch information
rstoyanchev committed Jul 13, 2021
1 parent 33f3aa9 commit bb816c1
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 4 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
Expand Down Expand Up @@ -29,6 +29,8 @@
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
Expand Down Expand Up @@ -70,6 +72,9 @@ public class HandlerMethod {
@Nullable
private final BeanFactory beanFactory;

@Nullable
private final MessageSource messageSource;

private final Class<?> beanType;

private final Method method;
Expand Down Expand Up @@ -101,6 +106,7 @@ public HandlerMethod(Object bean, Method method) {
Assert.notNull(method, "Method is required");
this.bean = bean;
this.beanFactory = null;
this.messageSource = null;
this.beanType = ClassUtils.getUserClass(bean);
this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
Expand All @@ -118,6 +124,7 @@ public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
Assert.notNull(methodName, "Method name is required");
this.bean = bean;
this.beanFactory = null;
this.messageSource = null;
this.beanType = ClassUtils.getUserClass(bean);
this.method = bean.getClass().getMethod(methodName, parameterTypes);
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method);
Expand All @@ -132,11 +139,23 @@ public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
* re-create the {@code HandlerMethod} with an initialized bean.
*/
public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
this(beanName, beanFactory, null, method);
}

/**
* Variant of {@link #HandlerMethod(String, BeanFactory, Method)} that
* also accepts a {@link MessageSource}.
*/
public HandlerMethod(
String beanName, BeanFactory beanFactory,
@Nullable MessageSource messageSource, Method method) {

Assert.hasText(beanName, "Bean name is required");
Assert.notNull(beanFactory, "BeanFactory is required");
Assert.notNull(method, "Method is required");
this.bean = beanName;
this.beanFactory = beanFactory;
this.messageSource = messageSource;
Class<?> beanType = beanFactory.getType(beanName);
if (beanType == null) {
throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
Expand All @@ -156,6 +175,7 @@ protected HandlerMethod(HandlerMethod handlerMethod) {
Assert.notNull(handlerMethod, "HandlerMethod is required");
this.bean = handlerMethod.bean;
this.beanFactory = handlerMethod.beanFactory;
this.messageSource = handlerMethod.messageSource;
this.beanType = handlerMethod.beanType;
this.method = handlerMethod.method;
this.bridgedMethod = handlerMethod.bridgedMethod;
Expand All @@ -174,6 +194,7 @@ private HandlerMethod(HandlerMethod handlerMethod, Object handler) {
Assert.notNull(handler, "Handler object is required");
this.bean = handler;
this.beanFactory = handlerMethod.beanFactory;
this.messageSource = handlerMethod.messageSource;
this.beanType = handlerMethod.beanType;
this.method = handlerMethod.method;
this.bridgedMethod = handlerMethod.bridgedMethod;
Expand All @@ -199,8 +220,13 @@ private void evaluateResponseStatus() {
annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
}
if (annotation != null) {
String reason = annotation.reason();
String resolvedReason = (StringUtils.hasText(reason) && this.messageSource != null ?
this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
reason);

this.responseStatus = annotation.code();
this.responseStatusReason = annotation.reason();
this.responseStatusReason = resolvedReason;
}
}

Expand Down
Expand Up @@ -258,7 +258,9 @@ protected void registerHandlerMethod(Object handler, Method method, T mapping) {
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
if (handler instanceof String) {
return new HandlerMethod((String) handler,
obtainApplicationContext().getAutowireCapableBeanFactory(), method);
obtainApplicationContext().getAutowireCapableBeanFactory(),
obtainApplicationContext(),
method);
}
return new HandlerMethod(handler, method);
}
Expand Down
Expand Up @@ -337,7 +337,9 @@ protected void registerHandlerMethod(Object handler, Method method, T mapping) {
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
if (handler instanceof String) {
return new HandlerMethod((String) handler,
obtainApplicationContext().getAutowireCapableBeanFactory(), method);
obtainApplicationContext().getAutowireCapableBeanFactory(),
obtainApplicationContext(),
method);
}
return new HandlerMethod(handler, method);
}
Expand Down
Expand Up @@ -22,13 +22,17 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletResponse;

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

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AliasFor;
Expand All @@ -47,12 +51,14 @@
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.testfixture.method.ResolvableMethod;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;

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

@Test
public void invokeAndHandle_responseStatusAndReasonCode() throws Exception {
Locale locale = Locale.ENGLISH;

String beanName = "handler";
StaticApplicationContext context = new StaticApplicationContext();
context.registerBean(beanName, Handler.class);
context.addMessage("BadRequest.error", locale, "Bad request message");
context.refresh();

ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
System.out.println(beanFactory.getType(beanName));

LocaleContextHolder.setLocale(locale);
try {
Method method = ResolvableMethod.on(Handler.class)
.named("responseStatusWithReasonCode")
.resolveMethod();

HandlerMethod handlerMethod = new HandlerMethod(beanName, beanFactory, context, method);
handlerMethod = handlerMethod.createWithResolvedBean();

new ServletInvocableHandlerMethod(handlerMethod)
.invokeAndHandle(this.webRequest, this.mavContainer);
}
finally {
LocaleContextHolder.resetLocaleContext();
}

assertThat(this.response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
assertThat(this.response.getErrorMessage()).isEqualTo("Bad request message");
}

@Test // gh-23775, gh-24635
public void invokeAndHandle_ETagFilterHasNoImpactWhenETagPresent() throws Exception {

Expand Down Expand Up @@ -417,6 +456,11 @@ public String responseStatusWithReason() {
return "foo";
}

@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "BadRequest.error")
public String responseStatusWithReasonCode() {
return "foo";
}

@ComposedResponseStatus(responseStatus = HttpStatus.BAD_REQUEST)
public void composedResponseStatus() {
}
Expand Down

0 comments on commit bb816c1

Please sign in to comment.