Handle ResponseStatusException thrown by MVC functional endpoints
Prior to this commit, exceptions thrown by MVC functional handlers would not be considered by `ExceptionHandlerExceptionResolver`. This means that common exceptions would not be handled consistently between annotated and functional handlers. This is true, for example, for all `ProblemDetails`-related exception handling. While MVC functional and annotation models are separate concepts, WebFlux has a different error handling model that processes all exceptions in a central place. This commit ensures that `ExceptionHandlerExceptionResolver` considers exceptions thrown by handlers of type `HandlerFunction<?>` and processes them accordingly. Closes gh-32689
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,6 +22,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.web.method.HandlerMethod;
|
import org.springframework.web.method.HandlerMethod;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
import org.springframework.web.servlet.function.HandlerFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for
|
* Abstract base class for
|
||||||
@@ -34,9 +35,9 @@ import org.springframework.web.servlet.ModelAndView;
|
|||||||
public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {
|
public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the handler is a {@link HandlerMethod} and then delegates to the
|
* Checks if the handler is a {@link HandlerMethod} or a {@link HandlerFunction}
|
||||||
* base class implementation of {@code #shouldApplyTo(HttpServletRequest, Object)}
|
* and then delegates to the base class implementation of {@code #shouldApplyTo(HttpServletRequest, Object)}
|
||||||
* passing the bean of the {@code HandlerMethod}. Otherwise returns {@code false}.
|
* passing the bean of the {@code HandlerMethod}. Otherwise, returns {@code false}.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
|
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
|
||||||
@@ -47,6 +48,9 @@ public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHan
|
|||||||
handler = handlerMethod.getBean();
|
handler = handlerMethod.getBean();
|
||||||
return super.shouldApplyTo(request, handler);
|
return super.shouldApplyTo(request, handler);
|
||||||
}
|
}
|
||||||
|
else if (handler instanceof HandlerFunction<?> handlerFunction) {
|
||||||
|
return super.shouldApplyTo(request, handlerFunction);
|
||||||
|
}
|
||||||
else if (hasGlobalExceptionHandlers() && hasHandlerMappings()) {
|
else if (hasGlobalExceptionHandlers() && hasHandlerMappings()) {
|
||||||
return super.shouldApplyTo(request, handler);
|
return super.shouldApplyTo(request, handler);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
|||||||
import org.springframework.web.servlet.DispatcherServlet;
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
import org.springframework.web.servlet.FlashMap;
|
import org.springframework.web.servlet.FlashMap;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
import org.springframework.web.servlet.function.HandlerFunction;
|
||||||
|
import org.springframework.web.servlet.function.ServerResponse;
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||||
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
|
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
|
||||||
@@ -82,6 +84,8 @@ import static org.mockito.Mockito.mock;
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
class ExceptionHandlerExceptionResolverTests {
|
class ExceptionHandlerExceptionResolverTests {
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
|
||||||
private static int DEFAULT_RESOLVER_COUNT;
|
private static int DEFAULT_RESOLVER_COUNT;
|
||||||
|
|
||||||
private static int DEFAULT_HANDLER_COUNT;
|
private static int DEFAULT_HANDLER_COUNT;
|
||||||
@@ -255,6 +259,19 @@ class ExceptionHandlerExceptionResolverTests {
|
|||||||
assertExceptionHandledAsBody(mav, "AnotherTestExceptionResolver: IllegalAccessException");
|
assertExceptionHandledAsBody(mav, "AnotherTestExceptionResolver: IllegalAccessException");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveExceptionGlobalHandlerForHandlerFunction() throws Exception {
|
||||||
|
loadConfiguration(MyConfig.class);
|
||||||
|
|
||||||
|
IllegalAccessException ex = new IllegalAccessException();
|
||||||
|
HandlerFunction<ServerResponse> handlerFunction = req -> {
|
||||||
|
throw new IllegalAccessException();
|
||||||
|
};
|
||||||
|
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerFunction, ex);
|
||||||
|
|
||||||
|
assertExceptionHandledAsBody(mav, "AnotherTestExceptionResolver: IllegalAccessException");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void resolveExceptionGlobalHandlerOrdered() throws Exception {
|
void resolveExceptionGlobalHandlerOrdered() throws Exception {
|
||||||
loadConfiguration(MyConfig.class);
|
loadConfiguration(MyConfig.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user