CodeGym /Java Course /Module 5. Spring /WebFlux Configuration

WebFlux Configuration

Module 5. Spring
Level 14 , Lesson 1
Available

The WebFlux Java configuration declares the components needed to process requests using annotated controllers or functional endpoints and provides an API for setting up the configuration. This means that you don't need to understand the underlying beans created by Java configuration.

If you need advanced settings that are not available in the configuration API, you can gain full control over the configuration using the advanced configuration mode.

Enabling WebFlux Configuration

You can use the @EnableWebFlux annotation in a Java configuration as shown in the following example:

Java

@Configuration
@EnableWebFlux
public class WebConfig {
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig

In the previous example, a number of Spring WebFlux infrastructure beans are registered and adapted to the dependencies available in the classpath - for JSON, XML and others formats.

WebFlux Configuration API

In your Java configuration, you can implement the WebFluxConfigurer interface, as shown in the following example:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
// Implement configuration methods...
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
// Implement configuration methods...
}

Conversion, formatting

By default, formatters are installed for various types of numbers and dates, along with tools to support customization via @NumberFormat and @DateTimeFormat for fields.

To register custom formatters and converters in the Java configuration, use the following:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
// ...
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addFormatters(registry: FormatterRegistry) {
// ...
}
}

By default, Spring WebFlux takes into account the request's locale when parsing and formatting date values. This is true for forms where dates are represented as strings with "input" form fields. However, for the "date" and "time" form fields, browsers use a fixed format defined in the HTML specification. For such cases, date and time formatting can be configured as follows:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addFormatters(registry: FormatterRegistry) {
val registrar = DateTimeFormatterRegistrar()
registrar.setUseIsoFormat(true)
registrar.registerFormatters(registry)
}
}

Validation

Default if in classpath a Bean Validation is present (e.g. Hibernate Validator), a LocalValidatorFactoryBean is registered as a global validator for use with the @Valid annotation and the Validated property on method arguments.

In Java configuration, you can configure a global Validator instance, as shown in the following example:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public Validator getValidator() {
// ...
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun getValidator(): Validator {
// ...
}
}

Note that it is also possible to register Validator implementations locally, as shown in the following example:

Java

@Controller
public class MyController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new FooValidator());
}
}
Kotlin

@Controller
class MyController {
@InitBinder
protected fun initBinder(binder: WebDataBinder) {
binder.addValidators(FooValidator())
}
}
If it is necessary that LocalValidatorFactoryBean was injected somewhere, create a bean and mark it with the @Primary annotation to avoid conflicting with what was declared in the MVC configuration.

Resolvers content types

You can configure how Spring WebFlux determines the requested media types for @Controller instances from a request. By default, only the Accept header is checked, but you can also enable a strategy based on request parameters.

The following example shows how to configure the permission of the requested content type:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureContentTypeResolver(RequestedContentTypeResolverBuilder builder) {
// ...
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureContentTypeResolver(builder: RequestedContentTypeResolverBuilder) {
// ...
}
}

HTTP message codecs

The following example shows how to configure the reading and writing of the request and response body:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().maxInMemorySize(512 * 1024);
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
// ...
}
}

ServerCodecConfigurer provides a default set of readers and writers. You can use it to add additional readers and writers, customize the default ones, or replace the default ones entirely.

For Jackson JSON and XML, consider using Jackson2ObjectMapperBuilder, which configures the Jackson library properties according to default to the following:

The following known modules are also automatically registered if they are found in the classpath:

View Recognizers

The following example shows how to configure view recognition:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// ...
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
// ...
}
}

ViewResolverRegistry contains abbreviations for the presentation technologies that the Spring Framework integrates with. The following example uses FreeMarker (which also requires customization of the underlying FreeMarker presentation technology):

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.freeMarker();
}
// Configure FreeMarker...
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("classpath:/templates");
return configurer;
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.freeMarker()
}
// Configure FreeMarker...
@Bean
fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
setTemplateLoaderPath("classpath:/templates")
}
}

You can also connect any ViewResolver implementation, as shown in the following example:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
ViewResolver resolver = ... ;
registry.viewResolver(resolver);
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
val resolver: ViewResolver = ...
registry.viewResolver(resolver
}
}

To provide support for content negotiation and rendering of other formats through view resolution (other than HTML), you can configure one or more default views based on the HttpMessageWriterView, which accepts any of the available codecs from spring-web. The following example shows how to do this:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.freeMarker();
Jackson2JsonEncoder encoder = new Jackson2JsonEncoder();
registry.defaultViews(new HttpMessageWriterView(encoder));
}
// ...
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.freeMarker()
val encoder = Jackson2JsonEncoder()
registry.defaultViews(HttpMessageWriterView(encoder))
}
// ...
}

Static Resources

This option provides a convenient way to handle static resources from a list of locations based on Resource.

In the following example, if the request starts with /resources, the relative path is used to find and process static resources relative to /static in the classpath. Resources are processed with an expiration date of one year to ensure maximum use of the browser cache and reduce the number of HTTP requests made by the browser. The Last-Modified header is also evaluated and, if present, a 304 status code is returned. The following listing shows an example:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
        .addResourceLocations("/public", "classpath:/static/")
        .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
registry.addResourceHandler("/resources/**")
        .addResourceLocations("/public", "classpath:/static/")
        .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
}
}

The resource handler also supports implementation chaining ResourceResolver and implementations ResourceTransformer, which can be used to create a toolkit for working with optimized resources.

You can use VersionResourceResolver for versioned resource URLs based on an MD5 hash derived from the content, immutable application version, or other information. ContentVersionStrategy (MD5 hash) is a thoughtful choice, but with some notable exceptions (such as JavaScript resources used with the module loader).

The following example shows how to use VersionResourceResolver in Java configuration:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
        .addResourceLocations("/public/")
        .resourceChain(true)
        .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
registry.addResourceHandler("/resources/**")
        .addResourceLocations("/public/")
        .resourceChain(true)
        .addResolver(VersionResourceResolver().addContentVersionStrategy("/**"))
}
}

You can use ResourceUrlProvider to rewrite URLs and applying the full chain of resolvers and resolvers (for example, to insert versions). WebFlux configuration provides a ResourceUrlProvider that can be injected into other beans.

Unlike Spring MVC, WebFlux currently does not have the ability to transparently rewrite static resource URLs because there are no presentation technologies which could use a non-blocking chain of resolvers and resolvers. When processing only local resources, a workaround is to use ResourceUrlProvider directly (for example, through a custom element) and block.

Note that when used as EncodedResourceResolver (e.g. Gzip, Brotli) and VersionedResourceResolver, they must be registered in an order that will reliably calculate versions based on the contents of the unencoded file.

WebJars are also supported using the WebJarsResourceResolver, which is automatically registered when the org.webjars:webjars-locator-core library is present in classpath. The resolver can rewrite URLs to include the jar version, as well as match incoming URLs without versions - for example, from /jquery/jquery.min.js to /jquery/1.2.0/jquery.min.js.

Java configuration based on ResourceHandlerRegistry provides additional capabilities for fine-grained control, such as last-change logic and optimized resource resolution.

Path Mapping

You can configure settings related to path mapping. For more information about individual parameters, see the javadoc at PathMatchConfigurer. The following example shows how to use PathMatchConfigurer:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer
    .setUseCaseSensitiveMatch(true)
    .setUseTrailingSlashMatch(false)
    .addPathPrefix("/api",
            HandlerTypePredicate.forAnnotation(RestController.class));
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Override
fun configurePathMatch(configurer: PathMatchConfigurer) {
configurer
    .setUseCaseSensitiveMatch(true)
    .setUseTrailingSlashMatch(false)
    .addPathPrefix("/api",
            HandlerTypePredicate.forAnnotation(RestController::class.java))
}
}

Spring WebFlux uses a parsed path representation a request called RequestPath to access the decoded values of path segments, with the semicolon-delimited contents removed (that is, path variables or matrix variables). This means that, unlike Spring MVC, there is no need to specify whether to decode the request path or remove semicolon-separated content to match paths.

Spring WebFlux also does not support suffix pattern matching, unlike Spring MVC, where we also recommend not to use it.

WebSocketService

In the WebFlux Java configuration, the WebSocketHandlerAdapter bean is declared , which provides support for calling WebSocket handlers. This means that all that is left to handle a WebSocket handshake request is to map the WebSocketHandler to a URL via SimpleUrlHandlerMapping.

In some cases, it may you will need to create a WebSocketHandlerAdapter bean with the provided WebSocketService service, which allows you to configure the properties of the server working with the WebSocket protocol. For example:

Java

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public WebSocketService getWebSocketService() {
TomcatRequestUpgradeStrategy strategy = new TomcatRequestUpgradeStrategy();
strategy.setMaxSessionIdleTimeout(0L);
return new HandshakeWebSocketService(strategy);
}
}
Kotlin

@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Override
fun webSocketService(): WebSocketService {
val strategy = TomcatRequestUpgradeStrategy().apply {
    setMaxSessionIdleTimeout(0L)
}
return HandshakeWebSocketService(strategy)
}
}

Advanced configuration mode

@EnableWebFlux imports DelegatingWebFluxConfiguration, which:

  • Pass the default Spring configuration for WebFlux applications

  • discovers and delegates authority to the WebFluxConfigurer implementation to set up this configuration.

In advanced mode, you can remove the @EnableWebFlux annotation code> and extend directly from DelegatingWebFluxConfiguration instead of implementing WebFluxConfigurer as shown in the following example:

Java

@Configuration
public class WebConfig extends DelegatingWebFluxConfiguration {
// ...
}
Kotlin

@Configuration
class WebConfig : DelegatingWebFluxConfiguration {
// ...
}

It is possible to save existing methods in WebConfig, but now it will also be possible to override bean declarations from base class, and still be able to have any number of other WebMvcConfigurer implementations in the classpath.

HTTP/2

HTTP/2 is supported in Reactor Netty, Tomcat, Jetty and Undertow. However, there are some caveats associated with the server configuration. More information can be found at wiki page over HTTP/2.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION