GH-3366: Return null from HTTP handleNoMatch

Fixes: https://github.com/spring-projects/spring-integration/issues/3366

When the same path is mapped for integration HTTP endpoint and MVC
method mapping, but different other mapping options (e.g. method)
and one of them fails to match, there is no way to try other
`RequestMapping` from the `DispatcherServlet` because
`RequestMappingHandlerMapping.handleNoMatch()` throws an exception
when no match instead of `null` according chain of responsibility logic
in the `DispatcherServlet`

*  Rework `IntegrationRequestMappingHandlerMapping.handleNoMatch()` to catch
all the super exception and return `null` to the `DispatcherServlet` to let
it to try other `RequestMapping` from the configuration
* Change an order for `IntegrationRequestMappingHandlerMapping` to `-1`
to let it to be tried first before regular MVC `RequestMappingHandlerMapping`
* Add a test-case to ensure that mix-in Integration HTTP and MVC for the
same path works as expected without failing on first try
This commit is contained in:
Artem Bilan
2020-09-10 12:12:45 -04:00
committed by Gary Russell
parent 09dec2eab5
commit 9aa9707f37
4 changed files with 50 additions and 16 deletions

View File

@@ -55,8 +55,7 @@ public class HttpIntegrationConfigurationInitializer implements IntegrationConfi
* which could also be overridden by the user by simply registering
* a {@link IntegrationRequestMappingHandlerMapping} {@code <bean>} with 'id'
* {@link HttpContextUtils#HANDLER_MAPPING_BEAN_NAME}.
* <p>
* In addition, checks if the {@code javax.servlet.Servlet} class is present on the classpath.
* <p> In addition, checks if the {@code javax.servlet.Servlet} class is present on the classpath.
* When Spring Integration HTTP is used only as an HTTP client, there is no reason to use and register
* the HTTP server components.
*/
@@ -64,9 +63,9 @@ public class HttpIntegrationConfigurationInitializer implements IntegrationConfi
if (HttpContextUtils.WEB_MVC_PRESENT &&
!registry.containsBeanDefinition(HttpContextUtils.HANDLER_MAPPING_BEAN_NAME)) {
BeanDefinitionBuilder requestMappingBuilder =
BeanDefinitionBuilder.genericBeanDefinition(IntegrationRequestMappingHandlerMapping.class);
requestMappingBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
requestMappingBuilder.addPropertyValue(IntegrationNamespaceUtils.ORDER, 0);
BeanDefinitionBuilder.genericBeanDefinition(IntegrationRequestMappingHandlerMapping.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.addPropertyValue(IntegrationNamespaceUtils.ORDER, -1);
registry.registerBeanDefinition(HttpContextUtils.HANDLER_MAPPING_BEAN_NAME,
requestMappingBuilder.getBeanDefinition());
}

View File

@@ -21,8 +21,10 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -177,6 +179,18 @@ public final class IntegrationRequestMappingHandlerMapping extends RequestMappin
return null;
}
@Override
protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> infos, String path, HttpServletRequest request) {
try {
return super.handleNoMatch(infos, path, request);
}
catch (ServletException ex) {
// Since this component has a higher precedence the 'null' return allows a
// 'DispatcherServlet' to try other 'HandlerMapping'
return null;
}
}
private static CorsConfiguration buildCorsConfiguration(CrossOrigin crossOrigin, RequestMappingInfo mappingInfo) {
CorsConfiguration config = new CorsConfiguration();
for (RequestMethod requestMethod : crossOrigin.getMethod()) {