Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
May 16, 2021 07:50 pm GMT

JAVA SpringBoot request filters

Intro

Filter provides a convenient mechanism for inspecting and filtering HTTP requests entering your application. For example, you want to inject a custom header to a request/response based on some conditions, or you want to run some checks before accessing the controller and serve the request.

Bad

Sometimes we see people implement those kinds of checks and assertions in the controller. yes, it works but it's not the best place to put it there.

Good

Use Filters which it runs before accessing the controllers and serve your requests.

Filters flow

filters-flow

Hands On

How to write?

In order to create a request filter. you just need to create a class that implements the Filter interface.

package com.ahmedash95.example.filters;import javax.servlet.*;@Componentpublic class RoomsCreateFilter implements Filter {  @Override  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {     // your code goes here.    // then you need to pass it in the chain.     chain.doFilter(request, response);  }}

If you are not familiar with the code above. here are some clarifications:

  • @Component is an annotation that allows us to register the class as a bean in the ApplicationContext
  • implements Filter interface tells Spring how this class should be used when register via @Component
  • import javax.servlet.*; imports Filter, ServletRequest, ServletResponse, FilterChain, and ServletException.
  • chain.doFilter(request, response); at the end is a must. as it tells spring how to continue handling the request. without it the response will be empty as the chain got broken.

Return response on errors

If you validate something and want to stop processing the request and just return the response. you can simply modify the Response object and return it from your Filter class.

@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {    boolean valid = false; // check for something    if (!valid) {        ((HttpServletResponse) response).setStatus(422);        response.getOutputStream().write("Validation error".getBytes());        return;    }    chain.doFilter(request, response);}

Examples

Let's write some real-world examples and some different scenarios to make sure you get the full picture.

1.Ensure API key is provided and valid

Let's say you are working on API that provides something. and you want to ensure that all requests to your application will have the API key and the key is valid. of course, you don't want to do the key validation to be in every controller and method.

Let's start by creating our filter first

package com.example.demo;import java.io.IOException;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.stereotype.Component;@Componentpublic class APIKeyValidatorFilter implements Filter {    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {        HttpServletRequest req = (HttpServletRequest) request;        String key = req.getHeader("X-API-KEY");        if(key == null) {            ((HttpServletResponse) response).setStatus(401);            response.getOutputStream().write("API Key is missing!".getBytes());            return;        }        if(!KeyValidator.valid(key)) {            ((HttpServletResponse) response).setStatus(403);            response.getOutputStream().write("API Key is invalid".getBytes());            return;        }        chain.doFilter(request, response);    }}

As you see. we basically do some checks. if the key is not provided we show the key missing error message. if is provided but not valid we show The key is an invalid message.

2.Ratelimit some endpoints

Let's say you want to protect the POST /comment endpoint. so your users must not be able to submit more than 2 comments in one minute. Again it can be done in the controller but It's not the best place to do it.

Let's create the Filter class

package com.example.demo;import javax.servlet.Filter;import java.io.IOException;import javax.servlet.*;import javax.servlet.http.HttpServletResponse;public class PostCommentRateLimit implements Filter{    RateLimiter rateLimiter;    public PostCommentRateLimit(RateLimiter rateLimiter) {        this.rateLimiter = rateLimiter;    }    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {        int userId = 1; // Get User        boolean valid = rateLimiter.validate(String.format("ratelimit.user.comments:%d", userId), 2);        if(!valid) {            ((HttpServletResponse) response).setStatus(429);            response.getOutputStream().write("Too Many Requests".getBytes());            return;        }        chain.doFilter(request, response);    }}

Few things to explain here:

  • We did not use @Component here. because we want to apply the filter on POST /comment endpoint only. so we will register it ourself next.
  • We have RateLimiter in constructor. and you don't need to worry about it. use whatever library fits your needs.

As mentioned we did not use @Component because it would apply the filter for all requests. instead we will create another class to register our filters the way we want.

package com.example.demo;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class RequestsFilterRegister {    @Autowired    RateLimiter rateLimiter;    @Bean    public FilterRegistrationBean<PostCommentRateLimit> registerPostCommentsRateLimiter(){        FilterRegistrationBean<PostCommentRateLimit> registrationBean  = new FilterRegistrationBean<>();        registrationBean.setFilter(new PostCommentRateLimit(rateLimiter));        registrationBean.addUrlPatterns("/comment");        return registrationBean;    }}
  • We created RequestsFilterRegister as @Configuration class
  • The only method there registerPostCommentsRateLimiter to register the filter as we want.
  • we used addUrlPatterns to apply the filter only on /comment endpoint.

Now we have one small problem. the filter is applied to any method on /comment either GET or POST. and to fix that we just need to modify the PostCommentRateLimit@doFilter to skip if the method is not post

HttpServletRequest req = (HttpServletRequest) request;if (!req.getMethod().equals("POST")) {    chain.doFilter(request, response);    return;}

Now the full class is

package com.example.demo;import javax.servlet.Filter;import java.io.IOException;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class PostCommentRateLimit implements Filter{    RateLimiter rateLimiter;    public PostCommentRateLimit(RateLimiter rateLimiter) {        this.rateLimiter = rateLimiter;    }    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {        HttpServletRequest req = (HttpServletRequest) request;        if (!req.getMethod().equals("POST")) {            chain.doFilter(request, response);            return;        }        int userId = 1; // Get User        boolean valid = rateLimiter.validate(String.format("ratelimit.user.comments:%d", userId), 2);        if(!valid) {            ((HttpServletResponse) response).setStatus(429);            response.getOutputStream().write("Too Many Requests".getBytes());            return;        }        chain.doFilter(request, response);    }}

Conclution

Now you know What is Request Filters, how to write them, and you applied some real-world examples. also you saw how to customize the routes and methods to apply filters on.

The full source code https://github.com/ahmedash95/java-spring-request-filters

For more, you can check baeldung.com/spring-boot-add-filter

Note

I'm still learning Java and spring. so if you see any mistakes or a better way in writing Filters Your comments and suggestions will be greatly appreciated.


Original Link: https://dev.to/ahmedash95/java-springboot-request-filters-15ha

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To