Learn the various ways to add Spring WebFlux Filters on annotation based Controllers as well as Functional Routers in Functional in Web Framework.
Tutorial Contents
Overview
Filters help centralizing a common piece of functionalities that are required to process Web Requests. Such functionalities include – authentication and authorization, logging, or timeouts etc. One of the main advantages of using filter is that we can add, remove or modify filters without changing the core request processors.
In this tutorial, we will learn to apply web filters in a Spring WebFlux application. That includes, using filters on annotation based controllers or on Functional Web Framework based request routers.
Setup
Let’s get a quick setup done before we write our own Filters. We will start from a basic Spring Boot application with required dependencies. Then we will create a controller endpoint or a functional route and add filters on it.
For a quicker setup, you can refer our article on How to Build Spring WebFlux Application.
Dependency
In order to create Spring WebFlux endpoints and write filters, we need spring-boot-starter-webflux dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Code language: HTML, XML (xml)
This dependency implicitly brings in all the other required dependencies.
WebFlux Controller Endpoint
Firstly, we will create a WebFlux annotation based controller endpoint. Once the endpoint is functional, we will apply filter on it.
@GetMapping("/students/{studentId}")
public Mono<Student> getStudent(@PathVariable String studentId) {
return service.getStudent(studentId);
}
Code language: Java (java)
WebFlux Routing Function
Similarly, using Spring 5 Functional Web Framework we can create a Functional Router to implement the same endpoint. Here, we are creating a Routing Function and attaching a Handler Function to it.
@Bean
public RouterFunction<ServerResponse> getStudentRoute() {
return route(GET("/students/{id}"),
request -> ok().body(service
.getStudent(request.pathVariable("id")), Student.class)
);
}
Code language: Java (java)
Types of WebFlux Filters
There are two types of filters available in Spring WebFlux – WebFilter and HandlerFilterFunction.
WebFlux WebFilter
WebFilter defines a contract to intercept and process Web Requests in a chained fashion. A WebFilter (documentation) act on a global level and once enabled they intercept all requests and responses. Having support for chaining, we can define multiple implementations of WebFilter and let them intercept requests in a chained sequence. We can use the WebFilters for the purpose of logging, timeouts, security, and applying transformations such as updating or setting new parameters or transforming the request URI etc. Most importantly, the WebFilter is applicable to both annotation based WebFlux Controllers and the Functional Web framework style Routing Functions.
Basically, the Web Filters are very similar to the Java Servlet Filters that they intercept requests and responses on a global level.
The WebFilter interface defines a single method – filter(ServerWebExchange, WebFilterChain). The ServerWebExchange instance provides access to the intercepted request and response. Using that we can access parameters and attributes, application context, user principal etc.
The other parameter WebFilterChain defines a chain of filters that are invoked in sequence. At the end of the chain it invokes the actual Controller or Routing Function. Each WebFilter implementation can use the WebFilterChain and delegate the processing to the next filter in the chain.
WebFlux HandlerFilterFunction
In Functional Style Routing a Router Function intercepts a request and invokes appropriate Handler Function. The functional routing API allows us to plugin zero or more HandlerFilterFunction (Documentation) instances that are applied before the HandlerFunction.
Remember that, the WebFlux filters of type HandlerFilterFunction are only available for Routing Functions defined by Spring 5 Functional Web Framework. In order to apply filters on Handler Functions, we need to provide an implementation of filter(ServerRequest, HandlerFunction) method of HandlerFilterFunction.
The ServerRequest instance provides access to request parameters, headers, cookies etc. While the HandlerFunction argument is an instance to the actual handler that the filter is applied. A filter function can invoke handler by providing the request instance and return the handler response. However, it can chose not to invoke the handler and return a custom response or throw any exception.
WebFlux WebFilter Example
For the purpose of demonstration, we will implement a WebFilter function that generates web statistics of total number of requests and total number of successful responses.
Example of Spring 5 WebFlux WebFilter Implementation
@Component
public class WebStatsFilter implements WebFilter {
@Autowired
private StatsService statsService;
@Override
public Mono<Void> filter(
ServerWebExchange serverWebExchange,
WebFilterChain webFilterChain) {
statsService.incrementRequestCounter();
if (Objects.requireNonNull(
serverWebExchange.getResponse().getStatusCode())
.is2xxSuccessful()) {
statsService.incrementSuccessCounter();
}
return webFilterChain.filter(serverWebExchange);
}
}
Code language: Java (java)
Here, we are incrementing the request counter upon each request to the application. Also, for every response with a success status code we increment the success counter.
WebFlux HandlerFilterFunction Example
As stated earlier HandlerFilterFunction is only applicable to route-based functional handlers. For the demonstration of HandlerFilterFunction we will create an authorization filter. The filter return Status Code of 403 (Forbidden), if the requesting user tries to access someone else’s information.
Example of Implementing HandlerFilterFunction
@Component
public class AuthorizationFilter
implements HandlerFilterFunction<ServerResponse, ServerResponse> {
@Override
public Mono<ServerResponse> filter(
ServerRequest serverRequest,
HandlerFunction handlerFunction) {
String userId = serverRequest.pathVariable("id");
String headerUserId = serverRequest.headers().firstHeader("userId");
return (userId.equals(headerUserId))
? handlerFunction.handle(serverRequest)
: ServerResponse.status(FORBIDDEN).build();
}
}
Code language: Java (java)
Now, we can plug in this filter on any handler function – as shown next.
@Bean
public RouterFunction<ServerResponse> getStudentRoute() {
return route(GET("/students/{id}"),
request -> ok().body(service
.getStudent(request.pathVariable("id")), Student.class)
).filter(authorizationFilter);
}
Code language: Java (java)
Alternatively, we can use lambda expression and attach the filter inline.
Example of attaching HandlerFilterFunction using Lambda Expression
@Bean
public RouterFunction<ServerResponse> getStudentRoute() {
return route(GET("/students/{id}"),
request -> ok().body(service
.getStudent(request.pathVariable("id")), Student.class)
).filter((request, handlerFunction) -> {
String userId = request.pathVariable("id");
String headerUserId = request.headers().firstHeader("userId");
return (userId.equals(headerUserId))
? handlerFunction.handle(request)
: ServerResponse.status(FORBIDDEN).build();
});
}
Code language: Java (java)
However, this approach is useful when the filter implementation is very specific to the handler function.
Summary
This quick tutorial covered examples of Spring WebFlux Filters. There are two types of filters in WebFlux – WebFilter and HandlerFilterFunction. The WebFilter is more similar to Servlet Filters and it works with both annotation based WebFlux Controllers, as well as functional router based Handler Functions. On the other hand the HandlerFilterFunction is specific to HandlerFunctions in Functional Web Framework.