'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 |
