1

I want to log the request params in the http request via Spring filter or aspect. I tried different ways but either request params are null or method is not called. I am using POSTMAN and it's a POST request

http://localhost:8080/AvailableData

sample request body :

{"keyUserAgent":"CFNetwork/1209 Darwin/20.2.0","locale":"en_US","eid":"8904977033","sessionId":"VGA-G20201030-776878787-1AD5-11EB-895C-H78789GJJH"}

method 1 : here "@Override" method of beforeRequest() is called but overloaded method that I created is not called(I added @RequestBody to get the body as per other solutions).

@Component
public class CustomLoggingFilter extends AbstractRequestLoggingFilter {
      
            protected void beforeRequest(HttpServletRequest request, String message,@RequestBody RequestDTO requestBody) {
            requestBody.getKeyUserAgent();
            requestBody.getEid();
            System.out.println("Eid: "+requestBody.getEid());
            System.out.println("getKeyUserAgent: "+requestBody.getKeyUserAgent());
            
        }  
        
    }

method 2 : here it is coming as null

@Aspect
@Component
@Order(1)
public class LogAspect {

    private final static Logger logger = LoggerFactory.getLogger(LoggerAspect.class);
      @Around("allControllerMethods() && args(..,@annotation(org.springframework...RequestBody) requestBody) ")
      public Object controllerEvents(ProceedingJoinPoint jp, Object requestBody) throws Throwable {
            
          ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
             HttpServletRequest request = attributes.getRequest();
             MethodSignature signature = (MethodSignature) jp.getSignature();
              Method method = signature.getMethod();
              Object resObject = jp.proceed();
              Object sessionId = attributes.getSessionId()
              if (requestBody != null) {
                  String keyUserAgent = request.getParameter("keyUserAgent");
                  System.out.println("keyUserAgent : " + keyUserAgent);
                  
                }
              
          return resObject;
      } 
vicky
  • 149
  • 2
  • 7
  • 21

2 Answers2

6

First of all, there is a problem with reading data from HttpRequest for logging and later for processing as class HttpServletRequest only allows to read its contents once, and any repeating attempt to read it will cause an Exception. So, spring boot provides a solution for that with usage of class ContentCachingRequestWrapper. The idea is that you read the entire context of your request once in the filter and copy the contents into your wrapper class that allows multiple reads. After that, you continue the chain with your wrapper class (that indirectly implements HttpServletRequest). So now in one of your filters (that must be configured after the filter that replaces HttpServletRequest with ContentCachingRequestWrapper you can read and log your request parameters and later on you still can read your request for handling it. I implemented this in our project and it works like a charm. Here are few links to the articles explaining how to do it. Reading HttpServletRequest Multiple Times in Spring, Java Code Examples for org.springframework.web.util.ContentCachingRequestWrapper

Michael Gantman
  • 7,315
  • 2
  • 19
  • 36
  • Thanks @Michael for sharing this insight. This is helpful and while it may do the job but it forces to create multiple request & filter classes. Is there an easier way to get this. Reason being I am doing this for enhancing the logging of the app, these many code changes will attract heavy QA and testing, if it does causes any impact to existing filters. Please advise. – vicky Jan 20 '21 at 06:57
  • Also one more thing it prints entire body but how to extract each item/key from json body like keyUserAgent, eid, sessionId ? – vicky Jan 20 '21 at 07:20
  • Okay checked and found in Controller the request body is coming as null and causing failure, so it doesn't works for me. – vicky Jan 20 '21 at 08:04
  • First of all, I don't know simpler solution and I guess there isn't one since Spring provides this one as their own standard solution. Second, if right now something doesn't work for you, Chances are that it is something in your code, since many people (including me) used this solution and it works for us. So, do some debugging, and you will probably find the cause of your problem – Michael Gantman Jan 20 '21 at 08:43
  • as you said it would be working as you might not be using "HttpServletRequest" in the other classes like controller, in my case there is a existing piece of code like controller that uses HttpServletRequest params(which I shouldn't touch ideally) so I guess unless that also starts using ContentCachingRequestWrapper it might not work in my case, right ? – vicky Jan 20 '21 at 10:20
  • no, definitely not. ContentCachingRequestWrapper is extention of HttpServletRequest. And that's a whole idea that in your first filter you receive real HttpServletRequest, read it and build your ContentCachingRequestWrapper with the same data as original HttpServletRequest. and then you send your ContentCachingRequestWrapper instead of HttpServletRequest that can no longer be read. However, since ContentCachingRequestWrapper is an extension of HttpServletRequest your controller still can use it and see it as just HttpServletRequest. – Michael Gantman Jan 20 '21 at 11:53
  • yes Michael you are right but in that case controller should use ContentCachingRequestWrapper not HttpServletRequest as latter can be used only once. My controller where I haven't changed to ContentCachingRequestWrapper is getting null request. Let me know if I am wrong. – vicky Jan 20 '21 at 13:12
  • Your filter that gets called before the controller will replace HttpServletRequest with ContentCachingRequestWrapper. Hoever, since ContentCachingRequestWrapper extends HttpServletRequest it means it is an instance of HttpServletRequest. So controller doesn't need any change. It still will receive HttpServletRequest where actual implementation of it will be ContentCachingRequestWrapper. – Michael Gantman Jan 20 '21 at 15:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/227602/discussion-between-vicky-and-michael-gantman). – vicky Jan 20 '21 at 15:14
0

TL;DR

How to get request body params in spring filter?

One good practice is to use javax.servlet filter chain is an interceptor which is abstraction that provided by the servlet container to giving a view into the invocation chain of a filtered request for a resource. Filters use the FilterChain to invoke the next filter in the chain, or if the calling filter is the last filter in the chain, to invoke the resource at the end of the chain.

@Component
static class RequestSizeFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain filterChain)
                    throws ServletException, IOException {
        ...
        //access HttpServletRequest request data 
        ...
        filterChain.doFilter(request, response);
    }

}

same functionality can be implemented with filter as well, see this example for proper bean registration.

Lunatic
  • 1,519
  • 8
  • 24