eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Overview

In this tutorial, we’ll go through the annotated HTTP filters the Micronaut framework provides. Initially, HTTP filters in Micronaut were closer to the Java EE Filter interface and the Spring Boot filters approach. But with the latest major version released, filters can now be annotation-based, separating filters for requests and responses.

In this tutorial, we’ll examine HTTP filters in Micronaut. More specifically, we’ll focus on the server filters introduced in version 4, the annotation-based filter methods.

2. HTTP Filters

HTTP filters were introduced as an interface in Java EE. It is a “specification” implemented in all Java web frameworks. As documented:

A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both.

Filters that implement the Java EE interface have a doFilter() method with 3 parameters, ServletRequest, ServletResponse, and FilterChain. This gives us access to the request object, and the response, and using the chain we pass the request and response to the next component. To this day, even newer frameworks still might use the same or similar names and parameters.

Some common real-life use cases that filters are very handy for:

  • Authentication filters
  • Header filter (to retrieve a value from a request or add a value in the response)
  • Metrics filters (eg when recording the request execution time)
  • Logging filters

3. HTTP Filters in Micronaut

HTTP filters in Micronaut follow in some way the Java EE Filter specs. For example, Micronaut’s HttpFilter interface provides a doFilter() method with a parameter for the request object and one for the chain object. The request parameter allows us to filter the request, and then use the chain object to process it and get back the response. Finally, changes can be made to the response object, if needed.

In Micronaut 4, some new annotations for filters were introduced, that offered filter methods for requests only, response only, or both.

Micronaut offers filters for our server requests received and responses sent, using the @ServerFilter. But it also offers filters for our REST clients, for requests against 3rd systems and microservices, using the @ClientFilter.

The Server filters have some concepts that make them very agile and useful:

  • accept some pattern to match the path we want to filter
  • can be ordered because some filters need to be executed before others (eg an authentication checking filter should be always first)
  • give options about filtering responses that might be in the type of error (like filtering throwables)

We’ll get into more detail about some of those concepts in the upcoming paragraphs.

4. Filter Patterns

HTTP filters in Micronaut are specific to endpoints based on their paths. To configure which endpoint a filter is applied to, we can set a pattern to match the path. The pattern can be of different styles, like ANT or REGEX, and the value, which is the actual pattern, like /endpoint*.

There are different options for pattern style, but the default is AntPathMatcher because it is more efficient performance-wise. Regex is a more powerful style to use when using patterns to match, but it is much slower than Ant. So we should use it only as a last option when Ant doesn’t support the style we’re looking for.

Some examples of styles we’ll need when using filters are:

  • /** will match any path
  • /filters-annotations/** will match all paths under `filters-annotations`, like /filters-annotations/endpoint1 and /filters-annotations/endpoint2
  • /filters-annotations/*1 will match all paths under `filters-annotations` but only when ending in ‘1’
  • **/endpoint1 will match all paths that end in ‘endpoint1’
  • **/endpoint* will match all paths that end with ‘endpoint’ plus anything extra at the end

where, in the default FilterPatternStyle.ANT style:

  • * matches zero or more characters
  • ** matches zero or more subdirectories in a path

5. Annotation-Based Server Filters in Micronaut

Annotated HTTP filters in Micronaut were added in Micronaut major version 4 and are also referred to as filter methods. Filter methods allow us to separate the specific filters for requests or responses. Before annotation-based filters, we only had one way to define filters and it was the same for filtering a request or a response. This way we can separate concerns, so keep our code cleaner and more readable.

Filter methods still allow us to define a filter that is both accessing a request AND modifying a response, if needed, using FilterContinuation.

5.1. Filter Methods

Based on whether we want to filter the request or response, we can use the @RequestFilter or @ResponseFilter annotations. On a class level, we still need an annotation to define the filter, the @ServerFilter. The path to filter and the order of filters are defined at the class level. We also have the option to apply path patterns per filter method.

Let’s combine all this info to create a ServerFilter that has one method that filters requests and another one that filters responses:

@Slf4j
@ServerFilter(patterns = { "**/endpoint*" })
public class CustomFilter implements Ordered {
    @RequestFilter
    @ExecuteOn(TaskExecutors.BLOCKING)
    public void filterRequest(HttpRequest<?> request) {
        String customRequestHeader = request.getHeaders()
          .get(CUSTOM_HEADER_KEY);
        log.info("request header: {}", customRequestHeader);
    }

    @ResponseFilter
    public void filterResponse(MutableHttpResponse<?> res) {
        res.getHeaders()
          .add(X_TRACE_HEADER_KEY, "true");
    }
}

The filterRequest() method is annotated with @RequestFilter and accepts an HTTPRequest parameter. This gives us access to the request. Then, it reads and logs a header in the request. In a real-life example, this could be doing more, like rejecting a request based on a header value passed.

The filterResponse() method is annotated with @ResponseFilter and accepts a MutableHttpResponse parameter, which is the response object we are about to return to the client. Before we respond though, this method adds a header in the response.

Keep in mind that the request could have already been processed by another filter we have, with a lower order, and might be processed by another filter next, with a higher order. Similarly, the response might have been processed by filters with higher order and the filters with lower order will be applied after. More on that in the Filter Order paragraph.

5.2. Continuations

The filter methods are a nice feature, to keep our code nice and clean. However, there is still the requirement to have methods that filter the same request and response. Micronaut provides continuations, to handle this requirement. The annotation on the method is the same as in requests, @RequestFilter, but the parameters are different. We also still have to use the @ServerFilter annotation on the class.

One typical example of a case in which we need to get access to a request and use the value on the response is the tracing header for the distributed tracing pattern, in distributed systems. On a high level, we use a header to trace down a request, so that we know on which exactly step it failed if it returns an error. For that, we need to pass in on every request/message a ‘request-id’ or ‘trace-id’ and if the service communicates with another service, it passes the same value:

@Slf4j
@ServerFilter(patterns = { "**/endpoint*" })
@Order(1)
public class RequestIDFilter implements Ordered {
    @RequestFilter
    @ExecuteOn(TaskExecutors.BLOCKING)
    public void filterRequestIDHeader(
        HttpRequest<?> request, 
        FilterContinuation<MutableHttpResponse<?>> continuation
    ) {
        String requestIdHeader = request.getHeaders().get(REQUEST_ID_HEADER_KEY);
        if (requestIdHeader == null || requestIdHeader.trim().isEmpty()) {
            requestIdHeader = UUID.randomUUID().toString();
            log.info(
                "request ID not received. Created and will return one with value: [{}]", 
                requestIdHeader
            );
        } else {
            log.info("request ID received. Request ID: [{}]", requestIdHeader);
        }

        MutableHttpResponse<?> res = continuation.proceed();

        res.getHeaders().add(REQUEST_ID_HEADER_KEY, requestIdHeader);
    }
}

The filterRequestIDHeader() method is annotated with @RequestFilter and has one HttpRequest and one FilterContinuation parameter. We get access to the request from the request parameter and check if the “Request-ID” header has a value. If not, we create one and we log the value in any case.

By using the continuation.proceed() method we get access to the response object. Then, we add the same header and value of the “Request-ID” header in the response, to be propagated up to the client.

5.3. Filter Order

In many use cases, it makes sense to have specific filters executed before or after others. HTTP filters in Micronaut provide two ways to handle the ordering of filter executions. One is the @Order Annotation and the other is implementing the Ordered interface. Both are on class level.

The way the ordering works is, that we provide an int value which is the order in which the filters will be executed. For request filters, it is straightforward. Order -5 will be executed before order 2 and order 2 will be executed before order 4. For response filters, it is the opposite. Order 4 will be applied first, then order 2 and finally order -5.

When we implement the interface, we need to manually override the getOrder() method. It defaults to zero:

@Filter(patterns = { "**/*1" })
public class PrivilegedUsersEndpointFilter implements HttpServerFilter, Ordered {
    // filter methods ommited

    @Override
    public int getOrder() {
        return 3;
    }
}

When we use the Annotation, we just have to set the value:

@ServerFilter(patterns = { "**/endpoint*" })
@Order(1)
public class RequestIDFilter implements Ordered {
    // filter methods ommited
}

Note, that testing the combination of @Order Annotation and implementing the Ordered interface has led to misbehavior, so it’s a good practice to choose one of the two methods and apply it everywhere.

6. Conclusion

In this tutorial, we examined the concept of filters in general and the HTTP filters in Micronaut. We saw the different options provided to implement a filter and some real-life use cases. Then, we presented examples of the annotated-based filters, for request-only filters, response-only filters, and both. Last, we spent some time on key concepts like the path patterns and the orders of the filters.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)