'Controller interceptor that process endpoint annotation in WebFlux

My team is in the middle of migrating our Spring MVC extensions to WebFlux.

We've got a feature that lets our clients customize metric of controller method. To do that we've created our annotation that is processed by HandlerInterceptorAdapter.

The problem is that I can't see any equivalent of this in Spring WebFlux. I can't use WebFilter because Spring does not know yet which endpoint will be called. How can I implement that?

The closest workaround I found is to use RequestMappingHandlerMapping and somehow build a map of Map<String(path), HandlerMethod>, but this is cumbersome and error prone in my opinion.

Is there any better way to solve this?

Edit:

It goes like this

public class MeteredHandlerInterceptor extends HandlerInterceptorAdapter {

public MeteredHandlerInterceptor() {
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // I save start time of method
    return true;
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    // I read endpoint method from the HandlerMethod, I apply any customisation by our custom @MeteredEndpoint annotation (for example custom name) and I save it in MeterRegistry
}
}

I haven't coded workaround yet because I didn't want to invest time in it, but I see that I could obtain HandlerMethod for path, but I'm not sure I will receive same HandlerMethod as I normally would when the controller is called.



Solution 1:[1]

Maybe little bit late, but it can still be useful for someone...

I have not found an easy way to do that, the best I was able to create is a HandlerAdapter bean that intercepts handling in the following way:

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public HandlerAdapter handlerAdapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
    return new HandlerAdapter() {

        @Override
        public boolean supports(Object handler) {
            return handler instanceof HandlerMethod;
        }

        @Override
        public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
            // your stuff here...
            // e.g. ((HandlerMethod) handler).getMethod().getAnnotations()...
            return requestMappingHandlerAdapter.handle(exchange, handler);
        }
    };
}

The idea is that this adapter is used for all HandlerMethod handlers (those are the ones created by collecting annotated methods from @Controllers) and delegates the handling to the RequestMappingHandlerAdapter (that would be used directly for HandlerMethod handlers in normal case, notice the @Order annotation here).

The point is you can put your code before/after the invocation of the handle method and you are aware of the method being invoked at this point.

Solution 2:[2]

Solution:

     @Component
 class AuditWebFilter(
      private val requestMapping: RequestMappingHandlerMapping
 ): WebFilter {

      override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
           // if not to call - then exchange.attributes will be empty
           // so little early initialize exchange.attributes by calling next line
           requestMapping.getHandler(exchange)

           val handlerFunction = exchange.attributes.get(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE) as HandlerMethod
           val annotationMethod = handlerFunction.method.getAnnotation(MyAnnotation::class.java)

           // annotationMethod  proccesing here
      }
 }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Ivan