This tutorial shows you how to create a custom argument resolver in Spring WebFlux using SyncHandlerMethodArgumentResolver
.
If you create an endpoint using Spring Boot and you want to store the values from a request to an object, there are various ways to do it. For example, you can create a class and add it as a parameter of the controller method, then Spring will inject the values from query parameters to an instance of the class. If the values come from the request body, just add @RequestBody
annotation. The @RequestHeader
annotation can be used if you want to get a value from the request header. What if you want to combine the values from the request params, body, headers, or cookies to an object. A possible solution is by creating a class that implements HandlerMethodArgumentResolver
.
You may already know the Pageable
interface in Spring. If you add certain query parameters in the request such as sort
and size
, the values will be injected to a Pageable
instance. The resolver of the Pageable
interface also implements
. In this tutorial, I am going to explain how to create a custom argument resolver in Spring WebFlux by implementing HandlerMethodArgumentResolver
HandlerMethodArgumentResolver
Create Custom Interface
For example, we want to get the name
, score
and version
values from a request. First, we need to create the interface.
package com.woolha.handlermethodargumentresolver.param;
public interface MyParameter {
String getName();
Integer getScore();
String getVersion();
}
Since we cannot create an object using only the interface, we need to create an instantiable class. Below is a class that implements the interface. To make the code short, I use Project Lombok in this tutorial.
package com.woolha.handlermethodargumentresolver.param;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
@Setter
public class MyParameterImpl implements MyParameter {
private String name;
private Integer score;
private String version;
}
Create Custom Argument Resolver Using SyncHandlerMethodArgumentResolver
In Spring WebFlux, you can create a custom argument resolver by creating a class that implements SyncHandlerMethodArgumentResolver
interface. There are two methods to be implemented: supportsParameter
and resolveArgumentValue
.
The supportsParameter
is used to check whether a method parameter should be handled using this argument resolver. Since this resolver is only for parameters whose type is MyParameter
, just check whether the type of the parameter equals to MyParameter
.
The SyncHandlerMethodArgumentResolver
has a method named resolveArgument
that returns a Mono
. If you look into the source code, the method has a default implementation that calls resolveArgumentValue
. Therefore, if you don't have any asynchronous operation to resolve the argument value, it's possible to override the resolveArgumentValue
method. If there is an asynchronous operation, it's also necessary to override the resolveArgument
method. In this example, we extract the values from headers and query parameters and return an object whose type is MyParameter
.
package com.woolha.handlermethodargumentresolver.support;
import com.woolha.handlermethodargumentresolver.param.MyParameter;
import com.woolha.handlermethodargumentresolver.param.MyParameterImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.NonNull;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.BindingContext;
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
@Slf4j
public class MyParameterArgumentResolver implements SyncHandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return MyParameter.class.equals(parameter.getParameterType());
}
@NonNull
@Override
public MyParameter resolveArgumentValue(
@NonNull MethodParameter parameter,
@NonNull BindingContext bindingContext,
@NonNull ServerWebExchange exchange
) {
final MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
final HttpHeaders headers = exchange.getRequest().getHeaders();
final String name = this.getNameValue(params);
final Integer score = this.getScoreValue(params);
final String version = this.getVersionValue(headers);
return MyParameterImpl.builder()
.name(name)
.score(score)
.version(version)
.build();
}
private String getNameValue(MultiValueMap<String, String> params) {
return params.get("name")
.stream()
.findFirst().orElse(null);
}
private Integer getScoreValue(MultiValueMap<String, String> params) {
try {
final String sizeParam = params.get("score").stream()
.findFirst().orElse(null);
if (sizeParam != null) {
return Integer.parseInt(sizeParam);
}
} catch (Exception e) {
log.warn("Unable to parse size param", e);
}
return null;
}
private String getVersionValue(HttpHeaders headers) {
return headers.getFirst("x-version-id");
}
}
The next thing you need to do is registering the custom argument resolver. It can be done in a @Configuration
-annotated class that implements WebFluxConfigurer
. You have to override the configureArgumentResolvers
method. The method has a parameter of type ArgumentResolverConfigurer
, which has addCustomResolver
method. To register the custom argument resolver above, call the addCustomResolver
method by passing the custom argument resolver bean.
package com.woolha.handlermethodargumentresolver.config;
import com.woolha.handlermethodargumentresolver.support.MyParameterArgumentResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
@Configuration
public class MyParameterConfig implements WebFluxConfigurer {
@Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
configurer.addCustomResolver(myParameterArgumentResolver());
}
@Bean
public MyParameterArgumentResolver myParameterArgumentResolver() {
return new MyParameterArgumentResolver();
}
}
Summary
You can create a custom argument resolver in Spring WebFlux by creating a class that extends SyncHandlerMethodArgumentResolver
. The class needs to override the supportsParameter
method which checks whether a parameter should be handled using the custom argument resolver. You also need to override the resolveArgumentValue
method. If there is an asynchronous operation for resolving the value, you have to override the resolveArgument
method which returns a Mono
. Finally, register the resolver using the addCustomResolver
method of ArgumentResolverConfigurer
in a configuration class by overriding the configureArgumentResolvers
method.