Skip to content

Commit 52af43d

Browse files
committed
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
1 parent 859b97c commit 52af43d

File tree

2 files changed

+25
-4
lines changed

2 files changed

+25
-4
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import org.springframework.lang.Nullable;
2323
import org.springframework.web.method.HandlerMethod;
2424
import org.springframework.web.servlet.ModelAndView;
25+
import org.springframework.web.servlet.function.HandlerFunction;
2526

2627
/**
2728
* Abstract base class for
@@ -34,9 +35,9 @@
3435
public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {
3536

3637
/**
37-
* Checks if the handler is a {@link HandlerMethod} and then delegates to the
38-
* base class implementation of {@code #shouldApplyTo(HttpServletRequest, Object)}
39-
* passing the bean of the {@code HandlerMethod}. Otherwise returns {@code false}.
38+
* Checks if the handler is a {@link HandlerMethod} or a {@link HandlerFunction}
39+
* and then delegates to the base class implementation of {@code #shouldApplyTo(HttpServletRequest, Object)}
40+
* passing the bean of the {@code HandlerMethod}. Otherwise, returns {@code false}.
4041
*/
4142
@Override
4243
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
@@ -47,6 +48,9 @@ else if (handler instanceof HandlerMethod handlerMethod) {
4748
handler = handlerMethod.getBean();
4849
return super.shouldApplyTo(request, handler);
4950
}
51+
else if (handler instanceof HandlerFunction<?> handlerFunction) {
52+
return super.shouldApplyTo(request, handlerFunction);
53+
}
5054
else if (hasGlobalExceptionHandlers() && hasHandlerMappings()) {
5155
return super.shouldApplyTo(request, handler);
5256
}

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java

+17
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@
6060
import org.springframework.web.servlet.DispatcherServlet;
6161
import org.springframework.web.servlet.FlashMap;
6262
import org.springframework.web.servlet.ModelAndView;
63+
import org.springframework.web.servlet.function.HandlerFunction;
64+
import org.springframework.web.servlet.function.ServerResponse;
6365
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
6466
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
6567
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
@@ -82,6 +84,8 @@
8284
@SuppressWarnings("unused")
8385
class ExceptionHandlerExceptionResolverTests {
8486

87+
//TODO
88+
8589
private static int DEFAULT_RESOLVER_COUNT;
8690

8791
private static int DEFAULT_HANDLER_COUNT;
@@ -255,6 +259,19 @@ void resolveExceptionGlobalHandler() throws Exception {
255259
assertExceptionHandledAsBody(mav, "AnotherTestExceptionResolver: IllegalAccessException");
256260
}
257261

262+
@Test
263+
void resolveExceptionGlobalHandlerForHandlerFunction() throws Exception {
264+
loadConfiguration(MyConfig.class);
265+
266+
IllegalAccessException ex = new IllegalAccessException();
267+
HandlerFunction<ServerResponse> handlerFunction = req -> {
268+
throw new IllegalAccessException();
269+
};
270+
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerFunction, ex);
271+
272+
assertExceptionHandledAsBody(mav, "AnotherTestExceptionResolver: IllegalAccessException");
273+
}
274+
258275
@Test
259276
void resolveExceptionGlobalHandlerOrdered() throws Exception {
260277
loadConfiguration(MyConfig.class);

0 commit comments

Comments
 (0)