Skip to content

Commit 983a8dc

Browse files
committed
Fix gh36481: Replace HandlerMappingIntrospector with PreFlightRequestHandler bean in MVC config
Signed-off-by: anaconda875 <hflbtmax@gmail.com>
1 parent 82a9d66 commit 983a8dc

4 files changed

Lines changed: 161 additions & 639 deletions

File tree

spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import org.springframework.context.ApplicationContextAware;
3636
import org.springframework.context.annotation.Bean;
3737
import org.springframework.context.annotation.Configuration;
38-
import org.springframework.context.annotation.Lazy;
3938
import org.springframework.core.KotlinDetector;
4039
import org.springframework.core.convert.converter.Converter;
4140
import org.springframework.format.Formatter;
@@ -61,6 +60,7 @@
6160
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
6261
import org.springframework.web.context.ServletContextAware;
6362
import org.springframework.web.cors.CorsConfiguration;
63+
import org.springframework.web.cors.PreFlightRequestHandler;
6464
import org.springframework.web.method.support.CompositeUriComponentsContributor;
6565
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
6666
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
@@ -76,8 +76,8 @@
7676
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
7777
import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
7878
import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
79+
import org.springframework.web.servlet.handler.DefaultPreFlightRequestHandler;
7980
import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite;
80-
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
8181
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
8282
import org.springframework.web.servlet.mvc.Controller;
8383
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
@@ -1161,11 +1161,9 @@ protected final Map<String, CorsConfiguration> getCorsConfigurations() {
11611161
protected void addCorsMappings(CorsRegistry registry) {
11621162
}
11631163

1164-
@SuppressWarnings("removal")
11651164
@Bean
1166-
@Lazy
1167-
public HandlerMappingIntrospector mvcHandlerMappingIntrospector() {
1168-
return new HandlerMappingIntrospector();
1165+
public PreFlightRequestHandler preFlightRequestHandler() {
1166+
return new DefaultPreFlightRequestHandler();
11691167
}
11701168

11711169
@Bean

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ private class CorsInterceptor implements HandlerInterceptor, CorsConfigurationSo
762762

763763
private final @Nullable CorsConfiguration config;
764764

765-
public CorsInterceptor(@Nullable CorsConfiguration config) {
765+
CorsInterceptor(@Nullable CorsConfiguration config) {
766766
this.config = config;
767767
}
768768

@@ -795,7 +795,7 @@ protected boolean invokeCorsProcessor(
795795
private final class PreFlightHttpRequestHandler
796796
extends CorsInterceptor implements HttpRequestHandler, PreFlightRequestHandler {
797797

798-
public PreFlightHttpRequestHandler(@Nullable CorsConfiguration config) {
798+
PreFlightHttpRequestHandler(@Nullable CorsConfiguration config) {
799799
super(config);
800800
}
801801

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright 2002-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.servlet.handler;
18+
19+
import jakarta.servlet.http.HttpServletRequest;
20+
import jakarta.servlet.http.HttpServletResponse;
21+
import org.jspecify.annotations.Nullable;
22+
import org.springframework.beans.factory.BeanFactoryUtils;
23+
import org.springframework.beans.factory.InitializingBean;
24+
import org.springframework.context.ApplicationContext;
25+
import org.springframework.context.ApplicationContextAware;
26+
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
27+
import org.springframework.core.io.ClassPathResource;
28+
import org.springframework.core.io.Resource;
29+
import org.springframework.core.io.support.PropertiesLoaderUtils;
30+
import org.springframework.http.server.RequestPath;
31+
import org.springframework.http.server.ServletServerHttpRequest;
32+
import org.springframework.util.Assert;
33+
import org.springframework.util.ClassUtils;
34+
import org.springframework.util.StringUtils;
35+
import org.springframework.web.cors.CorsUtils;
36+
import org.springframework.web.cors.PreFlightRequestHandler;
37+
import org.springframework.web.servlet.DispatcherServlet;
38+
import org.springframework.web.servlet.HandlerExecutionChain;
39+
import org.springframework.web.servlet.HandlerMapping;
40+
import org.springframework.web.servlet.NoHandlerFoundException;
41+
import org.springframework.web.util.ServletRequestPathUtils;
42+
43+
import java.io.IOException;
44+
import java.util.ArrayList;
45+
import java.util.Collections;
46+
import java.util.List;
47+
import java.util.Map;
48+
import java.util.Properties;
49+
50+
/**
51+
* Default implementation of {@link PreFlightRequestHandler} that discovers all
52+
* {@link HandlerMapping} beans in the {@link ApplicationContext} and uses them to
53+
* find a handler for a pre-flight CORS request, then delegates to the
54+
* {@link PreFlightRequestHandler} returned by that mapping.
55+
*
56+
* <p>Handler mappings are detected and sorted in the same way as in
57+
* {@link DispatcherServlet}, with fallback to the default mappings configured in
58+
* {@code DispatcherServlet.properties} if no mappings are found.
59+
*
60+
* @since 7.1
61+
* @see PreFlightRequestHandler
62+
* @see HandlerMapping
63+
* @see DispatcherServlet
64+
*/
65+
public class DefaultPreFlightRequestHandler
66+
implements PreFlightRequestHandler, ApplicationContextAware, InitializingBean {
67+
68+
private @Nullable ApplicationContext applicationContext;
69+
70+
private @Nullable List<HandlerMapping> handlerMappings;
71+
72+
@Override
73+
public void setApplicationContext(ApplicationContext applicationContext) {
74+
this.applicationContext = applicationContext;
75+
}
76+
77+
@Override
78+
public void afterPropertiesSet() {
79+
if (this.handlerMappings == null) {
80+
Assert.notNull(this.applicationContext, "No ApplicationContext");
81+
this.handlerMappings = initHandlerMappings(this.applicationContext);
82+
}
83+
}
84+
85+
private static List<HandlerMapping> initHandlerMappings(ApplicationContext context) {
86+
Map<String, HandlerMapping> beans =
87+
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
88+
89+
if (!beans.isEmpty()) {
90+
List<HandlerMapping> mappings = new ArrayList<>(beans.values());
91+
AnnotationAwareOrderComparator.sort(mappings);
92+
return Collections.unmodifiableList(mappings);
93+
}
94+
95+
return Collections.unmodifiableList(initFallback(context));
96+
}
97+
98+
private static List<HandlerMapping> initFallback(ApplicationContext applicationContext) {
99+
Properties properties;
100+
try {
101+
Resource resource = new ClassPathResource("DispatcherServlet.properties", DispatcherServlet.class);
102+
properties = PropertiesLoaderUtils.loadProperties(resource);
103+
}
104+
catch (IOException ex) {
105+
throw new IllegalStateException("Could not load DispatcherServlet.properties: " + ex.getMessage());
106+
}
107+
108+
String value = properties.getProperty(HandlerMapping.class.getName());
109+
String[] names = StringUtils.commaDelimitedListToStringArray(value);
110+
List<HandlerMapping> result = new ArrayList<>(names.length);
111+
for (String name : names) {
112+
try {
113+
Class<?> clazz = ClassUtils.forName(name, DispatcherServlet.class.getClassLoader());
114+
Object mapping = applicationContext.getAutowireCapableBeanFactory().createBean(clazz);
115+
result.add((HandlerMapping) mapping);
116+
}
117+
catch (ClassNotFoundException ex) {
118+
throw new IllegalStateException("Could not find default HandlerMapping [" + name + "]");
119+
}
120+
}
121+
return result;
122+
}
123+
124+
/**
125+
* Find the matching {@link HandlerMapping} for the request, and invoke the
126+
* handler it returns as a {@link PreFlightRequestHandler}.
127+
* @throws NoHandlerFoundException if no handler matches the request
128+
* @since 7.1
129+
*/
130+
@Override
131+
public void handlePreFlight(HttpServletRequest request, HttpServletResponse response) throws Exception {
132+
Assert.state(this.handlerMappings != null, "Not yet initialized via afterPropertiesSet.");
133+
Assert.state(CorsUtils.isPreFlightRequest(request), "Not a pre-flight request.");
134+
RequestPath previousPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
135+
try {
136+
ServletRequestPathUtils.parseAndCache(request);
137+
for (HandlerMapping mapping : this.handlerMappings) {
138+
HandlerExecutionChain chain = mapping.getHandler(request);
139+
if (chain != null) {
140+
Object handler = chain.getHandler();
141+
if (handler instanceof PreFlightRequestHandler preFlightHandler) {
142+
preFlightHandler.handlePreFlight(request, response);
143+
return;
144+
}
145+
throw new IllegalStateException("Expected PreFlightRequestHandler: " + handler.getClass());
146+
}
147+
}
148+
throw new NoHandlerFoundException(
149+
request.getMethod(), request.getRequestURI(), new ServletServerHttpRequest(request).getHeaders());
150+
}
151+
finally {
152+
ServletRequestPathUtils.setParsedRequestPath(previousPath, request);
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)