We often share few common validations, logging scenarios, modify data for every request/response. To cover these scenarios with some common code, we need to intercept the incoming request and the outgoing response.
To intercept the request-response, we can use the Filter interface provided in javax.servlet package.
Methods provided by Filter interface:
- init
- This method is invoked only once
@Override public void init(FilterConfig filterConfig) throws ServletException { }
- doFilter
- Invoked each time whenever client sends a request or server sends a response.
- We can perform all our logic in this method
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { }
- destroy
- clean up and removes filter from service
@Override public void destroy() { }
Dependencies that are needed to define filter in Spring-boot project comes from
- Gradle:
implementation 'org.springframework.boot:spring-boot-starter-web'
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.3.4.RELEASE</version> </dependency>
Use-case:
So in our example, We'll define a RequestValidationFilter which basically validates certain headers are present in the incoming request and sends it forward to the controller, if not it will throw an exception.
First, Let's define a simple controller which handles simple http calls:
AppController.java
package com.artifactsbyrake.pocs.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class AppController {
@GetMapping(value = "/hello")
public String getSayHello() {
return "GET - Hello Rake!!!";
}
@PostMapping(value = "/hello")
public String postSayHello() {
return "POST - Hello Rake!!!";
}
}
Now, let's define our filter which will intercept the requests to be handled by the controller
RequestValidationFilter.java
package com.artifactsbyrake.pocs.filter;
import com.artifactsbyrake.pocs.exceptions.MissingHeaderException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collections;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* This filter checks for required fields in headers needed to process the request
*/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RequestValidationFilter implements Filter {
public final String CUSTOM_HEADER = "CUSTOM_HEADER";
private final static Logger LOG = LoggerFactory.getLogger(RequestValidationFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
LOG.info("Initializing filter :{}", this);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// printing headers
req.getHeaderNames().asIterator().forEachRemaining(System.out::println);
//extract headers
HttpHeaders headers = Collections.list(req.getHeaderNames())
.stream()
.collect(Collectors.toMap(
Function.identity(),
h -> Collections.list(req.getHeaders(h)),
(oldValue, newValue) -> newValue,
HttpHeaders::new
));
//check for required header and throw exception if not found
if (!headers.containsKey(CUSTOM_HEADER) || CollectionUtils.isEmpty(headers.get(CUSTOM_HEADER))) {
throw new MissingHeaderException("Invalid Request - Missing header for " + CUSTOM_HEADER);
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
LOG.info("Destroying filter :{}", this);
}
}
Exception to throw for missing headers:
MissingHeaderException.java
package com.artifactsbyrake.pocs.exceptions;
public class MissingHeaderException extends RuntimeException {
private static final long serialVersionUID = 1L;
public MissingHeaderException(String message){
super(message);
}
}
time to test the magic 👽
- With header
- You can use n number of filters and order them
- Filter chain handles the order in which the filters needs to be applied
- Also there are ways you can use to apply specific filters to certain pattern of requests
Happy Coding 👨💻


Comments
Post a Comment