DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Spring Boot: Cross-Origin AJAX HTTP Requests
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • How To Build Web Service Using Spring Boot 2.x
  • How To Validate HTTP Post Request Body - Restful Web Services With Spring Framework | Spring Boot

Trending

  • Chat With Your Knowledge Base: A Hands-On Java and LangChain4j Guide
  • MCP Servers: The Technical Debt That Is Coming
  • GitHub Copilot's New AI Coding Agent Saves Developers Time – And Requires Their Oversight
  • The Future of Java and AI: Coding in 2025
  1. DZone
  2. Coding
  3. Frameworks
  4. Working With Filters in Spring

Working With Filters in Spring

Check out this post to learn more about working with filters in Spring.

By 
Jesus J. Puente user avatar
Jesus J. Puente
·
Updated Jun. 24, 19 · Tutorial
Likes (6)
Comment
Save
Tweet
Share
119.6K Views

Join the DZone community and get the full member experience.

Join For Free

In this article, I am going to talk about how to best implement filters over HTTP requests in Spring. That is, assuming we have a program listening in a URI, we can specify that we want to execute something before the requests are processed by the controller.

This is very useful if we want all the requests to meet a requirement, for example, that they must include a specific header.

As usual, the best way to understand the concept is by writing an example program and explaining it. The source of the program that I have written below is on my GitHub page.

In this program, there is a controller and listening REST requests in the class PrincipalCrontoller.java. This is the code for the class:

@RestController
public class PrincipalController {
@Autowired
SillyLog sillyLog;

@GetMapping("*")
public String entryOther(HttpServletRequest request,HttpServletResponse response)
{
sillyLog.debug("In entryOther");
if (response.getHeader("PROFE")!=null)
sillyLog.debug("Header contains PROFE: "+response.getHeader("PROFE"));
if (response.getHeader("CAKE")!=null)
sillyLog.debug("Header contains CAKE: "+response.getHeader("CAKE"));
return "returning by function entryOther\r\n"+
sillyLog.getMessage();
}
@GetMapping(value={"/","one"})
public String entryOne(HttpServletRequest request,HttpServletResponse response)
{
sillyLog.debug("In entryOne");
if (response.getHeader("PROFE")!=null)
{
sillyLog.debug("Header contains PROFE: "+response.getHeader("PROFE"));
return entryTwo(response);
}
return "returning by function entryOne\r\n"+
sillyLog.getMessage();
}
@GetMapping("two")
public String entryTwo(HttpServletResponse response)
{
sillyLog.debug("In entryTwo");
if (response.getHeader("PROFE")!=null)
sillyLog.debug("Header contains PROFE: "+response.getHeader("PROFE"));
return "returning by function entryTwo\r\n"+
sillyLog.getMessage();
}
@GetMapping("three")
public String entryThree()
{
sillyLog.debug("In entryThree");
return "returning by function entryThree\n"+
sillyLog.getMessage();
}
@GetMapping("redirected")
public String entryRedirect(HttpServletRequest request)
{
sillyLog.debug("In redirected");
return "returning by function entryRedirect\n"+
sillyLog.getMessage();
}
}


The entryOrderfunction will capture all GET requests, which the URIs aren't defined in the program. The entryOne function will be processing the GET requests to http://localhost:8080/one or http://localhost:8080/.

The SillyLog is a simple class where we add logs entries and then return them in the body response, so we will be able to see by where our request has gone.

In this application, it is defined as three filters: MyFilter.java, OtherFilter.java, and CakesFilter.java. The first one has preference over the second because of the parameter passed in the label @Order. I will speak more on the third filter later.

In the file MyFilter.java, we define our first filter as:

@Component
@Order(1)
public class MyFilter implements Filter{
@Autowired
SillyLog sillyLog;

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse  myResponse= (HttpServletResponse) response;
sillyLog.debug("Filter: URL"
+ " called: "+httpRequest.getRequestURL().toString());

if (httpRequest.getRequestURL().toString().endsWith("/one")){
myResponse.addHeader("PROFE", "FILTERED");
chain.doFilter(httpRequest, myResponse);
return;
}
        if (httpRequest.getRequestURL().toString().endsWith("/none")){   
            myResponse.setStatus(HttpStatus.BAD_GATEWAY.value());
    myResponse.getOutputStream().flush();
    myResponse.getOutputStream().println("-- I don't have any to tell you --");
            return; // No hago nada.
        }
if (httpRequest.getRequestURL().toString().endsWith("/redirect")){
myResponse.addHeader("PROFE", "REDIRECTED");
myResponse.sendRedirect("redirected");
chain.doFilter(httpRequest, myResponse);
return;
}
if (httpRequest.getRequestURL().toString().endsWith("/cancel")){
myResponse.addHeader("PROFE", "CANCEL");
myResponse.setStatus(HttpStatus.BAD_REQUEST.value());
myResponse.getOutputStream().flush();
myResponse.getOutputStream().println("-- Output by filter error --");
chain.doFilter(httpRequest, myResponse);
return;
}
chain.doFilter(request, response);
}
}


The OtherFilter class is a bit easier:

@Component
@Order(2)
public class OtherFilter implements Filter{
@Autowired
SillyLog sillyLog;

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
   HttpServletRequest httpRequest= (HttpServletRequest) request;
   HttpServletResponse  myResponse= (HttpServletResponse) response;
      sillyLog.debug("OtherFilter: URL"
      + " called: "+httpRequest.getRequestURL().toString());
   if (myResponse.getHeader("PROFE")!=null)
       sillyLog.debug("OtherFilter: Header contains PROFE: "+myResponse.getHeader("PROFE"));
   chain.doFilter(request, response);
   }
}


The first thing we have to do to define a general filter as it is tagged in the class with the label @Component. We should also implement the Filter interface. Our class could also extend from OncePerRequestFilter, which implements the interface Filter and adds some features. This is so that the filter is only executed once by request. In this example, we are going to simplify it to the maximum and we will directly implement the Filter interface.

The Filterinterface has three functions. void init(FilterConfig filterConfig) throws ServletException. This function will be executed by the web container, so it will be only executed once when the component is instantiated by Spring. void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException. This function will be executed each time an HTTP request is received. We will be able to see the content of the HTTP request in the ServletRequest object and we can modify the answer in the ServletResponse object. 

FilterChain will be used to continue the flow of the request. void destroy()  is called by the Spring web container to indicate to the filter that it will stop being active.

As I mentioned earlier, the label @Order allows us to specify the order in which each filter will be executed. In this case, the MyFilter filter will run before the OtherFilter filter.

MyFilter class performs different actions depending on the URL called. OtherFilter only adds a log when it is executed.

In the example code, we use only the doFilter function. First, it converts the ServletResponse class to HttpServletResponse and then the ServletRequest class to HttpServletRequest. This is necessary in order to be able to access certain properties of objects that would not otherwise be available.

I'm going to explain, step by step, the different cases contemplated in the MyFilter class, depending on the URL invoked.

  • /one: We add a PROFE header with the FILTERED value to the response. It is important to emphasize that we can only modify the response if the request is unalterable. This is after we run the DoFilter function of the chain class so that the flow is continued. In this case, we will run the second filter, and then, it throws the entryOne function of the controller.

The first line is returned by the entryTwo function. The logs added are shown below. I recommend watching the source code if you don't understand where are going out so many lines.

> curl  -s http://localhost:8080/one
returning by function entryTwo
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/1 Filter: URL called: http://localhost:8080/one
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/2 OtherFilter: URL called: http://localhost:8080/one
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/3 OtherFilter: Header contains PROFE: FILTERED
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/4 In entryOne
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/5 Header contains PROFE: FILTERED
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/6 In entryTwo
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/7 Header contains PROFE: FILTERED


> curl  -s http://localhost:8080/one
returning by function entryTwo
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/1 Filter: URL called: http://localhost:8080/one
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/2 OtherFilter: URL called: http://localhost:8080/one
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/3 OtherFilter: Header contains PROFE: FILTERED
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/4 In entryOne
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/5 Header contains PROFE: FILTERED
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/6 In entryTwo
SillyLog: 15eb34c2-cfac-4a27-9450-b3b07f44cb50/7 Header contains PROFE: FILTERED

This is better, isn't it? Now, in the headers, it shows our value for PROFE and we can see the order to redirect to http://localhost:8080/redirected. Note that the HTTP code returned is 302 (redirect). If we say for the curl to follow the redirection, passing it the -L parameter, we will see what we expected.

curl -L -s http://localhost:8080/redirect
returning by function entryRedirect
SillyLog: dcfc8b09-84a4-40a1-a2d6-43340abdf50c/1 Filter: URL called: http://localhost:8080/redirected
SillyLog: dcfc8b09-84a4-40a1-a2d6-43340abdf50c/2 OtherFilter: URL called: http://localhost:8080/redirected
SillyLog: dcfc8b09-84a4-40a1-a2d6-43340abdf50c/3 In redirected
  • /none . I put the HTTP code to return the value BAD_GATEWAY and I write in the body "I don't have any to tell you". I don't run the doFilterfunction, therefore, the second filter will not be called, nor would it be passed to the controller.

> curl  -s http://localhost:8080/none
-- I don't have any to tell you --


  • /cancel: I put the HTTP code to return the value BAD_REQUEST and I write in the body "Output by filter error" . I run the doFilterfunction therefore the OtherFilter filter will be executed. After the entryOtherin the controller, it will be run.

curl  -s http://localhost:8080/cancel
-- Output by filter error --
returning by function entryOther
SillyLog: 1cf7f7f9-1a9b-46a0-9b97-b8d5caf734bd/1 Filter: URL called: http://localhost:8080/cancel
SillyLog: 1cf7f7f9-1a9b-46a0-9b97-b8d5caf734bd/2 OtherFilter: URL called: http://localhost:8080/cancel
SillyLog: 1cf7f7f9-1a9b-46a0-9b97-b8d5caf734bd/3 OtherFilter: Header contains PROFE: CANCEL
SillyLog: 1cf7f7f9-1a9b-46a0-9b97-b8d5caf734bd/4 In entryOther
SillyLog: 1cf7f7f9-1a9b-46a0-9b97-b8d5caf734bd/5 Header contains PROFE: CANCEL


  •  /otros: In any another call, the doFilter function of the chain class is executed in MyFilter. After the second filter is executed, finally, some function in the controller will take control.

> curl -L -s http://localhost:8080/three
returning by function entryThree
SillyLog: a2dd979f-4779-4e34-b8f6-cae814370426/1 Filter: URL called: http://localhost:8080/three
SillyLog: a2dd979f-4779-4e34-b8f6-cae814370426/2 OtherFilter: URL called: http://localhost:8080/three
SillyLog: a2dd979f-4779-4e34-b8f6-cae814370426/3 In entryThree


To specify that a filter is only active for certain URLs, you must explicitly register it and not mark the class with the @Component tag. In this example, in the FiltrosApplication class, we see the function where a filter is added:

@Bean
public FilterRegistrationBean<CakesFilter> cakesFilter()
{
   FilterRegistrationBean<CakesFilter> registrationBean = new FilterRegistrationBean<>();
   registrationBean.setFilter(new CakesFilter());
   registrationBean.addUrlPatterns("/cakes/*");
   return registrationBean;
}


The CakesFilter is the next:

@Order(3)
public class CakesFilter implements Filter{
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletResponse  myResponse= (HttpServletResponse) response;
    myResponse.addHeader("CAKE", "EATEN");
    chain.doFilter(request, response);
  }
}


When you make a request to a URL that starts with  /cakes/*, you will see how the last filter is executed.

> curl  -s http://localhost:8080/cakes
returning by function entryOther
SillyLog: 41e2c9b9-f8d2-42cc-a017-08ea6089e646/1 Filter: URL called: http://localhost:8080/cakes
SillyLog: 41e2c9b9-f8d2-42cc-a017-08ea6089e646/2 OtherFilter: URL called: http://localhost:8080/cakes
SillyLog: 41e2c9b9-f8d2-42cc-a017-08ea6089e646/3 In entryOther
SillyLog: 41e2c9b9-f8d2-42cc-a017-08ea6089e646/4 Header contains CAKE: EATEN


Note: Because of Spring's way of managing its context variables, it is not possible to inject the SillyLog object with an @Autowired. If we inject it, we will see how the variable has the value null.

Don't forget to visit my page with more articles in Spanish about Spring. You can follow me in @chuchip.

Filter (software) Spring Framework Requests

Published at DZone with permission of Jesus J. Puente. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Spring Boot: Cross-Origin AJAX HTTP Requests
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • How To Build Web Service Using Spring Boot 2.x
  • How To Validate HTTP Post Request Body - Restful Web Services With Spring Framework | Spring Boot

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!