Atul Agrawal
Atul Agrawal

Reputation: 1520

How to log request body using spring boot and aspect

I have created an aspect to log the request body passed in a controller function:

This is what i am trying

@Pointcut("execution(* com.test.test.test.controller.*.* (..))")
  public void executeController() {}

  @Pointcut("execution(* com.test.test.common.exception.*.* (..))")
  public void executeExceptionAdvice() {}

  @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) || "
      + "@annotation(org.springframework.web.bind.annotation.PostMapping) || "
      + "@annotation(org.springframework.web.bind.annotation.PutMapping) ||"
      + "@annotation(org.springframework.web.bind.annotation.ExceptionHandler)")
  public void logRequestMapping() {}

  @Before("logRequestMapping() && executeController() && args(..,@RequestBody requestBody) ")
  public void logRequestBody(JoinPoint joinPoint, Object requestBody) {
    LOGGER = LoggerFactory.getLogger(joinPoint.getTarget().getClass());
    LOGGER.info("Method : {},Request Body:{}",
        LOGGER.getName() + "." + joinPoint.getSignature().getName(),
        MaskingUtil.jsonifyAndMask(requestBody));
  }

Now instead of matching with @RequestBody spring is matching arguments with .. i.e. generalize arguments and logging everything which is passed instead of the request body.I want to log only request body and if it is null the it won't print anything.

Upvotes: 0

Views: 9800

Answers (2)

kriegaex
kriegaex

Reputation: 67297

You already found my related answer, but I just noticed the link in the comment under the wrong answer here after preparing an example for you. BTW, I used pure POJO + AspectJ, not Spring AOP (only Spring libs on the classpath so as to be able to resolve the annotations), but the aspect should be the same there.

P.S.: My example is better suited to your problem because the pointcut here specifically matches target methods bearing the @RequestBody annotation. In the other example you would have to match all methods and then filter during runtime.

Dummy target classes:

I use two classes to test both of your package names. Otherwise they are the same.

package com.test.test.test.controller;

import org.springframework.web.bind.annotation.*;

public class MyController {
  // These should trigger the aspect
  @RequestMapping public String one(int number, @RequestBody String name) { return "Hey!"; }
  @PostMapping public void two(int number, @RequestBody String name) {}
  @PutMapping public String three(@RequestBody String name) { return "Ho!"; }
  @ExceptionHandler public void four(@RequestBody String name) {}

  // These should *not* trigger the aspect
  public String noAnnotation(@RequestBody String name) { return "No annotation"; }
  public String alsoNoAnnotation(String name) { return "Also no annotation"; }
  @RequestMapping public String five(int number, String name) { return "foo"; }
  @PostMapping public void six(int number, String name) {}
  @PutMapping public String seven(String name) { return "bar"; }
  @ExceptionHandler public void eight(String name) {}
}
package com.test.test.common.exception;

import org.springframework.web.bind.annotation.*;

public class MyExceptionHandler {
  // These should trigger the aspect
  @RequestMapping public String one(int number, @RequestBody String name) { return "Hey!"; }
  @PostMapping public void two(int number, @RequestBody String name) {}
  @PutMapping public String three(@RequestBody String name) { return "Ho!"; }
  @ExceptionHandler public void four(@RequestBody String name) {}

  // These should *not* trigger the aspect
  public String noAnnotation(@RequestBody String name) { return "No annotation"; }
  public String alsoNoAnnotation(String name) { return "Also no annotation"; }
  @RequestMapping public String five(int number, String name) { return "foo"; }
  @PostMapping public void six(int number, String name) {}
  @PutMapping public String seven(String name) { return "bar"; }
  @ExceptionHandler public void eight(String name) {}
}

Driver application:

package de.scrum_master.app;

import com.test.test.common.exception.MyExceptionHandler;
import com.test.test.test.controller.MyController;

public class Application {
  public static void main(String[] args) {
    MyController controller = new MyController();
    // These should trigger the aspect
    controller.one(1, "one");
    controller.two(2, "two");
    controller.three("three");
    controller.four("four");
    // These should *not* trigger the aspect
    controller.noAnnotation("none");
    controller.five(1, "five");
    controller.six(2, "six");
    controller.seven("seven");
    controller.eight("eight");
    controller.alsoNoAnnotation("none either");

    MyExceptionHandler handler = new MyExceptionHandler();
    // These should trigger the aspect
    handler.one(1, "one");
    handler.two(2, "two");
    handler.three("three");
    handler.four("four");
    // These should *not* trigger the aspect
    handler.noAnnotation("none");
    handler.five(1, "five");
    handler.six(2, "six");
    handler.seven("seven");
    handler.eight("eight");
    handler.alsoNoAnnotation("none either");
  }
}

Aspect:

package de.scrum_master.aspect;

import java.lang.annotation.Annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.bind.annotation.RequestBody;

@Aspect
public class MyAspect {
  @Pointcut("execution(* com.test.test.test.controller..*(.., @org.springframework.web.bind.annotation.RequestBody (*), ..))")
  public void executeController() {}

  @Pointcut("execution(* com.test.test.common.exception..*(.., @org.springframework.web.bind.annotation.RequestBody (*), ..))")
  public void executeExceptionAdvice() {}

  @Pointcut(
      "@annotation(org.springframework.web.bind.annotation.RequestMapping) || " +
      "@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
      "@annotation(org.springframework.web.bind.annotation.PutMapping) ||" +
      "@annotation(org.springframework.web.bind.annotation.ExceptionHandler)"
    )
  public void logRequestMapping() {}

  @Before(
    "logRequestMapping() &&" +
    "(executeController() || executeExceptionAdvice())"
  )
  public void logRequestBody(JoinPoint thisJoinPoint) {
    MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
    Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations();
    int index = -1;
    for (Annotation[] annotations : annotationMatrix) {
      index++;
      for (Annotation annotation : annotations) {
        if (!(annotation instanceof RequestBody))
          continue;
        Object requestBody = thisJoinPoint.getArgs()[index];
        System.out.println(thisJoinPoint);
        System.out.println("  Request body = " + requestBody);
      }
    }
  }
}

Console log:

execution(String com.test.test.test.controller.MyController.one(int, String))
  Request body = one
execution(void com.test.test.test.controller.MyController.two(int, String))
  Request body = two
execution(String com.test.test.test.controller.MyController.three(String))
  Request body = three
execution(void com.test.test.test.controller.MyController.four(String))
  Request body = four
execution(String com.test.test.common.exception.MyExceptionHandler.one(int, String))
  Request body = one
execution(void com.test.test.common.exception.MyExceptionHandler.two(int, String))
  Request body = two
execution(String com.test.test.common.exception.MyExceptionHandler.three(String))
  Request body = three
execution(void com.test.test.common.exception.MyExceptionHandler.four(String))
  Request body = four

Upvotes: 2

Oreste Viron
Oreste Viron

Reputation: 3805

Maybe you should write the full path of @RequestBody :

@annotation(org.springframework...RequestBody) requestBody

Upvotes: 0

Related Questions