Browse Source

Set embeddedValueResolver on WebConversionService

Closes gh-8923
pull/49721/merge
Moritz Halbritter 2 years ago committed by Moritz Halbritter
parent
commit
a04b65ea74
  1. 19
      core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/WebConversionService.java
  2. 21
      core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/format/WebConversionServiceTests.java
  3. 14
      module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxAutoConfiguration.java
  4. 20
      module/spring-boot-webflux/src/test/java/org/springframework/boot/webflux/autoconfigure/WebFluxAutoConfigurationTests.java
  5. 14
      module/spring-boot-webmvc/src/main/java/org/springframework/boot/webmvc/autoconfigure/WebMvcAutoConfiguration.java
  6. 20
      module/spring-boot-webmvc/src/test/java/org/springframework/boot/webmvc/autoconfigure/WebMvcAutoConfigurationTests.java

19
core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/WebConversionService.java

@ -31,6 +31,7 @@ import org.springframework.format.number.money.Jsr354NumberFormatAnnotationForma @@ -31,6 +31,7 @@ import org.springframework.format.number.money.Jsr354NumberFormatAnnotationForma
import org.springframework.format.number.money.MonetaryAmountFormatter;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringValueResolver;
/**
* {@link org.springframework.format.support.FormattingConversionService} dedicated to web
@ -53,7 +54,23 @@ public class WebConversionService extends DefaultFormattingConversionService { @@ -53,7 +54,23 @@ public class WebConversionService extends DefaultFormattingConversionService {
* @since 2.3.0
*/
public WebConversionService(DateTimeFormatters dateTimeFormatters) {
super(false);
this(null, dateTimeFormatters);
}
/**
* Create a new WebConversionService that configures formatters with the provided
* date, time, and date-time formats, or registers the default if no custom format is
* provided. The given {@code embeddedValueResolver} is used to resolve embedded
* values such as property placeholders in
* {@link org.springframework.format.annotation.DateTimeFormat#pattern()} patterns.
* @param embeddedValueResolver the embedded value resolver to use, or {@code null}
* @param dateTimeFormatters the formatters to use for date, time, and date-time
* formatting
* @since 4.1.0
*/
public WebConversionService(@Nullable StringValueResolver embeddedValueResolver,
DateTimeFormatters dateTimeFormatters) {
super(embeddedValueResolver, false);
if (dateTimeFormatters.isCustomized()) {
addFormatters(dateTimeFormatters);
}

21
core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/format/WebConversionServiceTests.java

@ -29,8 +29,12 @@ import java.time.format.FormatStyle; @@ -29,8 +29,12 @@ import java.time.format.FormatStyle;
import java.util.Calendar;
import java.util.Date;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.format.annotation.DateTimeFormat;
import static org.assertj.core.api.Assertions.assertThat;
/**
@ -178,10 +182,27 @@ class WebConversionServiceTests { @@ -178,10 +182,27 @@ class WebConversionServiceTests {
assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isOne();
}
@Test
void embeddedValueResolverIsAppliedToAnnotationPattern() throws Exception {
WebConversionService conversionService = new WebConversionService(
(value) -> value.replace("${my.date.format}", "yyyy-MM-dd"), new DateTimeFormatters());
TypeDescriptor dateType = new TypeDescriptor(FormattedDate.class.getDeclaredField("date"));
Date date = Date.from(ZonedDateTime.of(2000, 1, 2, 3, 4, 5, 6, ZoneId.of("UTC")).toInstant());
assertThat(conversionService.convert(date, dateType, TypeDescriptor.valueOf(String.class)))
.isEqualTo("2000-01-02");
}
private void customDateFormat(Object input) {
WebConversionService conversionService = new WebConversionService(
new DateTimeFormatters().dateFormat("dd*MM*yyyy"));
assertThat(conversionService.convert(input, String.class)).isEqualTo("01*01*2018");
}
static class FormattedDate {
@DateTimeFormat(pattern = "${my.date.format}")
@Nullable Date date;
}
}

14
module/spring-boot-webflux/src/main/java/org/springframework/boot/webflux/autoconfigure/WebFluxAutoConfiguration.java

@ -59,6 +59,7 @@ import org.springframework.boot.webflux.autoconfigure.WebFluxProperties.Apiversi @@ -59,6 +59,7 @@ import org.springframework.boot.webflux.autoconfigure.WebFluxProperties.Apiversi
import org.springframework.boot.webflux.autoconfigure.WebFluxProperties.Format;
import org.springframework.boot.webflux.filter.OrderedHiddenHttpMethodFilter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@ -73,6 +74,7 @@ import org.springframework.http.CacheControl; @@ -73,6 +74,7 @@ import org.springframework.http.CacheControl;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringValueResolver;
import org.springframework.validation.Validator;
import org.springframework.web.accept.ApiVersionParser;
import org.springframework.web.filter.reactive.HiddenHttpMethodFilter;
@ -312,7 +314,8 @@ public final class WebFluxAutoConfiguration { @@ -312,7 +314,8 @@ public final class WebFluxAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ WebProperties.class, ServerProperties.class })
@ImportRuntimeHints(WebFluxValidatorRuntimeHints.class)
static class EnableWebFluxConfiguration extends DelegatingWebFluxConfiguration {
static class EnableWebFluxConfiguration extends DelegatingWebFluxConfiguration
implements EmbeddedValueResolverAware {
private final WebFluxProperties webFluxProperties;
@ -322,6 +325,8 @@ public final class WebFluxAutoConfiguration { @@ -322,6 +325,8 @@ public final class WebFluxAutoConfiguration {
private final @Nullable WebFluxRegistrations webFluxRegistrations;
private @Nullable StringValueResolver embeddedValueResolver;
EnableWebFluxConfiguration(WebFluxProperties webFluxProperties, WebProperties webProperties,
ServerProperties serverProperties, ObjectProvider<WebFluxRegistrations> webFluxRegistrations) {
this.webFluxProperties = webFluxProperties;
@ -334,7 +339,7 @@ public final class WebFluxAutoConfiguration { @@ -334,7 +339,7 @@ public final class WebFluxAutoConfiguration {
@Override
public FormattingConversionService webFluxConversionService() {
Format format = this.webFluxProperties.getFormat();
WebConversionService conversionService = new WebConversionService(
WebConversionService conversionService = new WebConversionService(this.embeddedValueResolver,
new DateTimeFormatters().dateFormat(format.getDate())
.timeFormat(format.getTime())
.dateTimeFormat(format.getDateTime()));
@ -342,6 +347,11 @@ public final class WebFluxAutoConfiguration { @@ -342,6 +347,11 @@ public final class WebFluxAutoConfiguration {
return conversionService;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
@Bean
@Override
public Validator webFluxValidator() {

20
module/spring-boot-webflux/src/test/java/org/springframework/boot/webflux/autoconfigure/WebFluxAutoConfigurationTests.java

@ -74,10 +74,12 @@ import org.springframework.context.i18n.LocaleContext; @@ -74,10 +74,12 @@ import org.springframework.context.i18n.LocaleContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.format.Parser;
import org.springframework.format.Printer;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.CacheControl;
import org.springframework.http.ResponseCookie;
@ -340,6 +342,17 @@ class WebFluxAutoConfigurationTests { @@ -340,6 +342,17 @@ class WebFluxAutoConfigurationTests {
});
}
@Test
void embeddedValueResolverIsAppliedToConversionService() throws Exception {
this.contextRunner.withPropertyValues("my.date.format=yyyy-MM-dd").run((context) -> {
FormattingConversionService conversionService = context.getBean(FormattingConversionService.class);
TypeDescriptor dateType = new TypeDescriptor(FormattedDate.class.getDeclaredField("date"));
Date date = Date.from(ZonedDateTime.of(2000, 1, 2, 3, 4, 5, 6, ZoneId.of("UTC")).toInstant());
assertThat(conversionService.convert(date, dateType, TypeDescriptor.valueOf(String.class)))
.isEqualTo("2000-01-02");
});
}
@Test
void validatorWhenNoValidatorShouldUseDefault() {
this.contextRunner.run((context) -> {
@ -1333,4 +1346,11 @@ class WebFluxAutoConfigurationTests { @@ -1333,4 +1346,11 @@ class WebFluxAutoConfigurationTests {
}
static class FormattedDate {
@DateTimeFormat(pattern = "${my.date.format}")
@Nullable Date date;
}
}

14
module/spring-boot-webmvc/src/main/java/org/springframework/boot/webmvc/autoconfigure/WebMvcAutoConfiguration.java

@ -67,6 +67,7 @@ import org.springframework.boot.webmvc.autoconfigure.WebMvcProperties.Apiversion @@ -67,6 +67,7 @@ import org.springframework.boot.webmvc.autoconfigure.WebMvcProperties.Apiversion
import org.springframework.boot.webmvc.autoconfigure.WebMvcProperties.Apiversion.Use;
import org.springframework.boot.webmvc.autoconfigure.WebMvcProperties.Format;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -87,6 +88,7 @@ import org.springframework.util.AntPathMatcher; @@ -87,6 +88,7 @@ import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringValueResolver;
import org.springframework.validation.DefaultMessageCodesResolver;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
@ -438,7 +440,8 @@ public final class WebMvcAutoConfiguration { @@ -438,7 +440,8 @@ public final class WebMvcAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
@ImportRuntimeHints(MvcValidatorRuntimeHints.class)
static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration
implements ResourceLoaderAware, EmbeddedValueResolverAware {
private final Resources resourceProperties;
@ -450,6 +453,8 @@ public final class WebMvcAutoConfiguration { @@ -450,6 +453,8 @@ public final class WebMvcAutoConfiguration {
private final @Nullable WebMvcRegistrations mvcRegistrations;
private @Nullable StringValueResolver embeddedValueResolver;
@SuppressWarnings("NullAway.Init")
private ResourceLoader resourceLoader;
@ -566,7 +571,7 @@ public final class WebMvcAutoConfiguration { @@ -566,7 +571,7 @@ public final class WebMvcAutoConfiguration {
@Override
public FormattingConversionService mvcConversionService() {
Format format = this.mvcProperties.getFormat();
WebConversionService conversionService = new WebConversionService(
WebConversionService conversionService = new WebConversionService(this.embeddedValueResolver,
new DateTimeFormatters().dateFormat(format.getDate())
.timeFormat(format.getTime())
.dateTimeFormat(format.getDateTime()));
@ -574,6 +579,11 @@ public final class WebMvcAutoConfiguration { @@ -574,6 +579,11 @@ public final class WebMvcAutoConfiguration {
return conversionService;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
@Bean
@Override
public Validator mvcValidator() {

20
module/spring-boot-webmvc/src/test/java/org/springframework/boot/webmvc/autoconfigure/WebMvcAutoConfigurationTests.java

@ -78,11 +78,13 @@ import org.springframework.context.annotation.Import; @@ -78,11 +78,13 @@ import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.format.Parser;
import org.springframework.format.Printer;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
@ -509,6 +511,17 @@ class WebMvcAutoConfigurationTests { @@ -509,6 +511,17 @@ class WebMvcAutoConfigurationTests {
});
}
@Test
void embeddedValueResolverIsAppliedToConversionService() throws Exception {
this.contextRunner.withPropertyValues("my.date.format=yyyy-MM-dd").run((context) -> {
FormattingConversionService conversionService = context.getBean(FormattingConversionService.class);
TypeDescriptor dateType = new TypeDescriptor(FormattedDate.class.getDeclaredField("date"));
Date date = Date.from(ZonedDateTime.of(2000, 1, 2, 3, 4, 5, 6, ZoneId.of("UTC")).toInstant());
assertThat(conversionService.convert(date, dateType, TypeDescriptor.valueOf(String.class)))
.isEqualTo("2000-01-02");
});
}
@Test
void noMessageCodesResolver() {
this.contextRunner.run(
@ -1756,4 +1769,11 @@ class WebMvcAutoConfigurationTests { @@ -1756,4 +1769,11 @@ class WebMvcAutoConfigurationTests {
}
static class FormattedDate {
@DateTimeFormat(pattern = "${my.date.format}")
@Nullable Date date;
}
}

Loading…
Cancel
Save