In some scenarios, we need to get HandlerMethod
ahead of org.springframework.web.reactive.DispatcherHandler, such as an implement of authentication by WebFilter
public class AuthFilter implements WebFilter, Ordered {
@Autowired
private RequestMappingHandlerMapping handlerMapping;
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return handlerMapping.getHandler(exchange).switchIfEmpty(chain.filter(exchange))
.flatMap(handler -> {
if (handler instanceof HandlerMethod) {
HandlerMethod methodHandle = (HandlerMethod) handler;
CheckPermission permission = methodHandle.getMethodAnnotation(CheckPermission.class);
if (Objects.isNull(permission)) {
permission = AnnotationUtils.findAnnotation(methodHandle.getBeanType(), CheckPermission.class);
}
if (Objects.nonNull(permission)) {
// TODO do something..
}
}
return chain.filter(exchange);
}
);
}
}
HandlerMethod
is necessary but there is no alternative way to get it withon WebFilter, which limited the capabilities of WebFilter. so, we need call getHandler
, however this may lead to repeated call of lookupHandlerMethod and unexpected execution of exchange.getAttributes().remove(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
as below:
https://github.com/spring-projects/spring-framework/blob/00da70e26b9cb5ece2a9499e7c1b449e856b4aea/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java#L293
To avoid duplicate search for HandlerMethod
in mapping.getHandler
, I suggest implementing a improved getHandlerInternal, in which HandlerMethod
would be keep in attributes as long as it found.
@Override
public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) {
return Mono.justOrEmpty(exchange.<HandlerMethod>getAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE))
.switchIfEmpty(Mono.defer(() -> getHandlerAndCache(exchange)));
}
protected Mono<HandlerMethod> getHandlerAndCache(ServerWebExchange exchange) {
exchange.getAttributes().remove(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
return super.getHandlerInternal(exchange)
.doOnNext(handler -> exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handler))
.doOnTerminate(() -> ProducesRequestCondition.clearMediaTypesAttribute(exchange));
}
related issue:https://github.com/spring-projects/spring-framework/issues/20123