Search code examples
javaspringaopreactive

Joinpoint on method with return type Flux/Mono returns blank


I am new to reactive java and working on spring boot project. I have a method in controller class, which takes id and return Mono. its works fine and retrun response with data.But It returns blank and and 200 status when I uncomment a line @ValidateParam(name = StringValidator.class) annotation in this method.

  @GetMapping("/vendor/id/{id}")
   //@ValidateParam(name = StringValidator.class)
    public Mono<Vendor> getVendor(@PathVariable("id") String id){
        //get specific vendor
       return vendorService.getVendorByID(id);
    }

I have created my custom validator to validate the id, I receive as param to this method.I am running the validator using pointcut. I check the joint point and if there is no error then I use proceed method from ProceedingJoinPoint

Any help is much appreciated? I can not understand what is wrong here.

Upon debug, I find when there is no validation error in id, method proceeds and I can print the data when I subscribe to Mono. But it return blank when I return same mono.

Here is how validate param method looks like:

     @Around(value = "@annotation(ValidateParam)")
        public void validationProcessror(ProceedingJoinPoint method , ValidateParam validateParam) throws Throwable {
            Object arg = method.getArgs()[0];
            Class validatorclass = validateParam.name();
            ValidationError err = applyValidation((Validator) validatorclass.getConstructor().newInstance(), arg);
            if(err.size() == 0) {
                method.proceed();
            }else {
                throw new RuntimeException(err.errors.toString());
            }
        }

Solution

  • Your @Around advice returns null, because it is a void method, which would only make sense when intercepting void methods. If you study Spring AOP or AspectJ manuals, you will find out that, in contrast to @Before and @After adbvice types which are always void because they return nothing, an @Around advice needs to return something copmpatible with ther intercepted method's return type.

    Your advice should rather look like this (untested due to incomplete sample code):

    @Around("@annotation(validateParam)")
    public Object validationProcessror(ProceedingJoinPoint joinPoint, ValidateParam validateParam) throws Throwable {
      Object arg = joinPoint.getArgs()[0];
      Class validatorclass = validateParam.name();
      ValidationError err = applyValidation((Validator) validatorclass.getConstructor().newInstance(), arg);
      if (err.size() == 0)
        return joinPoint.proceed();
      else
        throw new RuntimeException(err.errors.toString());
    }
    

    Please also note that if you want to bind the annotation value to an advice method sargument, you need to use the argument name validateParam (lower-case) rather than the class name ValidateParam.

    There are other things which could be optimised in your advice, but I want to avoid throwing too much information at you.


    Update: If you always want to pass on the original method parameters to your target method and return the original value without modification, you do not need an @Around advice at all. @Before is enough and simplifies your code:

    @Before("@annotation(validateParam)")
    public void validationProcessror(JoinPoint joinPoint, ValidateParam validateParam) {
      Object arg = joinPoint.getArgs()[0];
      Class validatorclass = validateParam.name();
      ValidationError err = applyValidation((Validator) validatorclass.getConstructor().newInstance(), arg);
      if (err.size() > 0)
        throw new RuntimeException(err.errors.toString());
    }
    

    Update 2: I have never used any reactive frameworks before, but I got a hunch that throwing a runtime error might not be the right thing to do in the first place. This is, of course, unrelated to the problem at hand, but might cause you trouble later on. Maybe, your @Around advice ought to return Mono.error() in that situation. But I could be wrong, being a reactive noob.