Spring @вокруг вызовов WebClient

#spring #kotlin #aop #spring-webflux

#весна #kotlin #aop #spring-webflux

Вопрос:

Я пытаюсь создать атрибут, который будет проверять Captcha в запросе через атрибут. Суть проблемы в том, что я не могу найти способ обработки асинхронного вызова WebClient в @Around() обработчике

Цель

 @Captcha
@PostMapping(path = ["some-endpoint"])
fun doSomething(@RequestBody request: Mono<MyRequestWithCaptchaReponseField>) : Mono<MyResponse> { 
  // Endpoint Code
}
  

Код AOP

 @Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Captcha()

@Aspect
@Component
class CaptchaAspect {
  @Autowired
  private lateinit var captchaClient: CaptchaService

  @Around("@annoation(Captcha)")
  @Throws(Throwable::class)
  fun validateCaptcha(joinPoint: ProceedingJoinPoint): Mono<Any> {

     val request = joinPoint.args[0] as Mono<MyRequestWithCaptchaReponseField> // This works
     return request.map { captchaClient.isValid(it.captchaResponse } // This seems to be making the call. I would throw an exception here if the captcha was invalid
                   .flatMap { joinPoint.proceed() as Mono<Any> }

  }
}
  

Это настолько близко, насколько я понял, и это почти работает, но возвращаемый ответ — a mono<MyResponse> , и кажется, что, хотя конечная точка контроллера возвращает a Mono<MyResponse> , в качестве @Around() метода он ожидает возврата только MyResponse .

Основной вопрос заключается в том, что если мне нужно выполнить вызов API WebClient внутри @Around() метода, как мне обработать возврат joinPoint.proceed после завершения вызова API?

Комментарии:

1. В чем проблема с этим? Вы получаете сообщение об ошибке?

2. Этот парень на самом деле говорит, что ответ не был возвращен, что меня немного смущает. Если я изменю эту последнюю плоскую карту на карту, она вернет Mono и сериализует его в качестве ответа. У него просто есть поле, доступное для сканирования

Ответ №1:

Я нашел решение

 @Aspect
@Component
class CaptchaAspect {
  @Autowired
  private lateinit var captchaClient: CaptchaService

  @Around("@annoation(Captcha)")
  @Throws(Throwable::class)
  fun validateCaptcha(joinPoint: ProceedingJoinPoint): Mono<Any> {

     val request = joinPoint.args[0] as Mono<MyRequestWithCaptchaReponseField> // This works
     return joinPoint.proceed(arrayOf<Any>(request.filterWhen { r -> {
       if (r is MyRequestWithCaptchaReponseField) {
         this.captchaClient.isValid(r.captchaResponse)
       } else {
         throw Exception("Request should have a captcha field")
       }
     })
  }
}
  

ProceedingJoinPoint.proceed может принимать массив, который сопоставляется с аргументами действия контроллера. Таким образом, вы можете перехватить arg[0] и настроить свою логику перед ее отправкой. Эти шаги конвейера выполняются перед чем-либо в методе контроллера. Я изменил свой вызов captchaClient.isValid на фильтр, который всегда либо выдает исключение, либо возвращает Mono.just(true)