#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)