Add mappedHandler Predicate to AbstractHandlerExceptionResolver

Closes gh-26772
This commit is contained in:
rstoyanchev 2023-12-05 11:46:10 +00:00
parent 753409083d
commit dd23b1d156
2 changed files with 49 additions and 14 deletions

View File

@ -17,6 +17,7 @@
package org.springframework.web.servlet.handler; package org.springframework.web.servlet.handler;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
@ -53,6 +54,9 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
private int order = Ordered.LOWEST_PRECEDENCE; private int order = Ordered.LOWEST_PRECEDENCE;
@Nullable
private Predicate<Object> mappedHandlerPredicate;
@Nullable @Nullable
private Set<?> mappedHandlers; private Set<?> mappedHandlers;
@ -74,13 +78,24 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
return this.order; return this.order;
} }
/**
* Use a {@code Predicate} to determine which handlers this exception
* resolver applies to, including when the request was not mapped in which
* case the handler is {@code null}.
* <p>If no handler predicate, nor handlers, nor handler classes are set,
* the exception resolver applies to all handlers.
* @since 6.1.2
*/
public void setMappedHandlerPredicate(Predicate<Object> predicate) {
this.mappedHandlerPredicate =
(this.mappedHandlerPredicate != null ? this.mappedHandlerPredicate.and(predicate) : predicate);
}
/** /**
* Specify the set of handlers that this exception resolver should apply to. * Specify the set of handlers that this exception resolver should apply to.
* <p>The exception mappings and the default error view will only apply to the specified handlers. * <p>If no handler predicate, nor handlers, nor handler classes are set,
* <p>If no handlers or handler classes are set, the exception mappings and the default error * the exception resolver applies to all handlers.
* view will apply to all handlers. This means that a specified default error view will be used * @see #setMappedHandlerPredicate(Predicate)
* as a fallback for all exceptions; any further HandlerExceptionResolvers in the chain will be
* ignored in this case.
*/ */
public void setMappedHandlers(Set<?> mappedHandlers) { public void setMappedHandlers(Set<?> mappedHandlers) {
this.mappedHandlers = mappedHandlers; this.mappedHandlers = mappedHandlers;
@ -88,19 +103,18 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
/** /**
* Specify the set of classes that this exception resolver should apply to. * Specify the set of classes that this exception resolver should apply to.
* <p>The exception mappings and the default error view will only apply to handlers of the * The resolver will only apply to handlers of the specified types; the
* specified types; the specified types may be interfaces or superclasses of handlers as well. * specified types may be interfaces or superclasses of handlers as well.
* <p>If no handlers or handler classes are set, the exception mappings and the default error * <p>If no handler predicate, nor handlers, nor handler classes are set,
* view will apply to all handlers. This means that a specified default error view will be used * the exception resolver applies to all handlers.
* as a fallback for all exceptions; any further HandlerExceptionResolvers in the chain will be * @see #setMappedHandlerPredicate(Predicate)
* ignored in this case.
*/ */
public void setMappedHandlerClasses(Class<?>... mappedHandlerClasses) { public void setMappedHandlerClasses(Class<?>... mappedHandlerClasses) {
this.mappedHandlerClasses = mappedHandlerClasses; this.mappedHandlerClasses = mappedHandlerClasses;
} }
/** /**
* Add a mapped handler class. * Alternative to {@link #setMappedHandlerClasses(Class[])}.
* @since 6.1 * @since 6.1
*/ */
public void addMappedHandlerClass(Class<?> mappedHandlerClass) { public void addMappedHandlerClass(Class<?> mappedHandlerClass) {
@ -177,6 +191,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
/** /**
* Check whether this resolver is supposed to apply to the given handler. * Check whether this resolver is supposed to apply to the given handler.
* <p>The default implementation checks against the configured * <p>The default implementation checks against the configured
* {@linkplain #setMappedHandlerPredicate(Predicate) handlerPredicate}
* {@linkplain #setMappedHandlers handlers} and * {@linkplain #setMappedHandlers handlers} and
* {@linkplain #setMappedHandlerClasses handler classes}, if any. * {@linkplain #setMappedHandlerClasses handler classes}, if any.
* @param request current HTTP request * @param request current HTTP request
@ -188,6 +203,9 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
* @see #setMappedHandlerClasses * @see #setMappedHandlerClasses
*/ */
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) { protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
if (this.mappedHandlerPredicate != null) {
return this.mappedHandlerPredicate.test(handler);
}
if (handler != null) { if (handler != null) {
if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) { if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
return true; return true;
@ -205,11 +223,13 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
/** /**
* Whether there are any handler mappings registered via * Whether there are any handler mappings registered via
* {@link #setMappedHandlers(Set)} or {@link #setMappedHandlerClasses(Class[])}. * {@link #setMappedHandlers(Set)}, {@link #setMappedHandlerClasses(Class[])}, or
* {@link #setMappedHandlerPredicate(Predicate)}.
* @since 5.3 * @since 5.3
*/ */
protected boolean hasHandlerMappings() { protected boolean hasHandlerMappings() {
return (this.mappedHandlers != null || this.mappedHandlerClasses != null); return (this.mappedHandlers != null || this.mappedHandlerClasses != null ||
this.mappedHandlerPredicate != null);
} }
/** /**

View File

@ -385,6 +385,21 @@ public class ExceptionHandlerExceptionResolverTests {
assertExceptionHandledAsBody(mav, "DefaultTestExceptionResolver: IllegalStateException"); assertExceptionHandledAsBody(mav, "DefaultTestExceptionResolver: IllegalStateException");
} }
@Test // gh-26772
void resolveExceptionViaMappedHandlerPredicate() throws Exception {
Object handler = new Object();
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class);
this.resolver.setMappedHandlerPredicate(h -> h == handler);
this.resolver.setApplicationContext(ctx);
this.resolver.afterPropertiesSet();
ModelAndView mav = this.resolver.resolveException(
this.request, this.response, handler, new IllegalStateException());
assertExceptionHandledAsBody(mav, "DefaultTestExceptionResolver: IllegalStateException");
}
private void assertMethodProcessorCount(int resolverCount, int handlerCount) { private void assertMethodProcessorCount(int resolverCount, int handlerCount) {
assertThat(this.resolver.getArgumentResolvers().getResolvers()).hasSize(resolverCount); assertThat(this.resolver.getArgumentResolvers().getResolvers()).hasSize(resolverCount);