0

I have a requirement wherein I need to save any request and response being passed to a Rest Controller in a database, so I decided to implement Spring AOP for the same. The issue that is now being encountered is I couldn't get hold of any of the request body. Request body is being retrieved as an empty string. My Rest Controllers are all wrapped inside a Base Object and AOP only detects the outer Base class.

My Base Class :

public class BaseObject<T> {
    private T data;
}

My controller methods have definition like this :

@PostMapping("/student")
    public BaseObject<StudentResponse> saveStudent(@RequestBody BaseObject<StudentRequest> studentRequest) {

What I have tried till now :

private void saveRequestInDatabase(ProceedingJoinPoint joinPoint, long time) {
        System.out.println("****************Inside saveRequestInDatabase method*****************");
        System.out.println(Thread.currentThread().getName());
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        DataBaseRequest dataBaseRequest = new DataBaseRequest();
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        dataBaseRequest.setMethod(className + "." + methodName + "()"); // class name and method name retrieved fine
        
        Object[] args = joinPoint.getArgs();
        System.out.println("Object Arguments :- " +joinPoint.getArgs()); // only has the Outer BaseObject, the data inside it is null
        
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paramNames = u.getParameterNames(method);
        System.out.println("Param Names :- "+paramNames);
        if (args != null && paramNames != null) {
            String params = "";
            for (int i = 0; i < args.length; i++) {
                params += "  " + paramNames[i] + ": " + args[i]; // ***paramNames[] has the studentRequest variable name*** 
                                                                // ***args[] has the outer BaseObject but the data inside it, is null***
            }
            dataBaseRequest.setParams(params);
        }
        
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        ContentCachingRequestWrapper req = new ContentCachingRequestWrapper(request);
        byte[] requestBody = req.getContentAsByteArray();
        String reqBodyStr = new String(requestBody, StandardCharsets.UTF_8);
        JsonObject jsonObject = new JsonObject();
        String reqBody = null;
        try {
            reqBody = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        jsonObject.addProperty("requestBody", reqBody); // ***requestBody is "" when being executed***
        jsonObject.addProperty("reqBody", reqBodyStr); // ***reqBody is "" when being executed***

        dataBaseRequest.setEndPoint(request.getServletPath()); // This is fetched fine
        dataBaseRequest.setOperation(request.getMethod()); // fetched fine
        dataBaseRequestService.saveRequest(dataBaseRequest);// saved inside database but the column to store reqObject has {data:null}
        System.out.println("((((((((((((((((Done saving request)))))))))))))))))))))");
    }

I have also tried this :

@Before("execution(your.package.where.is.endpoint.*.*(..)) && args(reqArgs)")

Even here, reqArgs = {BaseObject@21216}, data = null.

The only place I could see my request body is: Inside the HttpServletRequest request object, there is an attribute named CachedContent = {ByteArrayOutputStream@13221} which has my JSON request object that is being passed from Postman, i.e, all the attributes of StudentRequest object, but I don't know how to grab hold of the same.

Please let me know how can I get the request objects (StudentObject) and response objects (StudentResponse) when it is wrapped around the BaseObject.

Thanks!

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • You should ideally be using a Filter , do read through this [blog](https://blog.devgenius.io/how-to-build-spring-filters-for-api-cross-cutting-concerns-6e52ac667c0) – R.G Jun 24 '22 at 04:37
  • Sure, will go through it @R.G – MysteriousCoder Jun 24 '22 at 07:19
  • But @R.G, AOP should also be able to do the same. The question is how it is failing to fetch the request body, because approach wise AOP is strong as well I feel so. – MysteriousCoder Jun 24 '22 at 15:00
  • 1
    Without extra coding a [request body cannot be read multiple times](https://stackoverflow.com/questions/34804205/how-can-i-read-request-body-multiple-times-in-spring-handlermethodargumentresol) which could be one major thing you need to solve when going ahead with AOP based approach. A [Filter](https://docs.oracle.com/javaee/6/api/javax/servlet/Filter.html#:~:text=A%20filter%20is%20an%20object,filtering%20in%20the%20doFilter%20method.) on the other hand fits the exact design/code requirement you have here . – R.G Jun 25 '22 at 03:35
  • For some additional points to consider , do read the summary section of this [blog](https://programmer.ink/think/springboot-foundation-of-microservice-series-filter-interceptor-and-aop-aspect.html) – R.G Jun 25 '22 at 03:39

0 Answers0