Skip to content

Commit 67330df

Browse files
committed
Expose mapped handler as an exchange attribute
Issue: SPR-15564
1 parent 58a5e7f commit 67330df

File tree

6 files changed

+57
-39
lines changed

6 files changed

+57
-39
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/HandlerMapping.java

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,31 @@
3131
public interface HandlerMapping {
3232

3333
/**
34-
* Name of the {@link ServerWebExchange} attribute that contains the
35-
* best matching pattern within the handler mapping.
36-
* <p>Note: This attribute is not required to be supported by all
37-
* HandlerMapping implementations. URL-based HandlerMappings will
38-
* typically support it, but handlers should not necessarily expect
39-
* this request attribute to be present in all scenarios.
34+
* Name of the {@link ServerWebExchange#getAttributes() attribute} that
35+
* contains the mapped handler for the best matching pattern.
36+
*/
37+
String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
38+
39+
/**
40+
* Name of the {@link ServerWebExchange#getAttributes() attribute} that
41+
* contains the best matching pattern within the handler mapping.
4042
*/
4143
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
4244

4345
/**
44-
* Name of the {@link ServerWebExchange} attribute that contains the path
45-
* within the handler mapping, in case of a pattern match, or the full
46-
* relevant URI (typically within the DispatcherServlet's mapping) else.
46+
* Name of the {@link ServerWebExchange#getAttributes() attribute} that
47+
* contains the path within the handler mapping, in case of a pattern match
48+
* such as {@code "/static/**"} or the full relevant URI otherwise.
4749
* <p>Note: This attribute is not required to be supported by all
4850
* HandlerMapping implementations. URL-based HandlerMappings will
49-
* typically support it, but handlers should not necessarily expect
51+
* typically support it but handlers should not necessarily expect
5052
* this request attribute to be present in all scenarios.
5153
*/
5254
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
5355

5456
/**
55-
* Name of the {@link ServerWebExchange} attribute that contains the URI
56-
* templates map, mapping variable names to values.
57+
* Name of the {@link ServerWebExchange#getAttributes() attribute} that
58+
* contains the URI templates map mapping variable names to values.
5759
* <p>Note: This attribute is not required to be supported by all
5860
* HandlerMapping implementations. URL-based HandlerMappings will
5961
* typically support it, but handlers should not necessarily expect
@@ -62,8 +64,9 @@ public interface HandlerMapping {
6264
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
6365

6466
/**
65-
* Name of the {@link ServerWebExchange} attribute that contains a map with
66-
* URI matrix variables.
67+
* Name of the {@link ServerWebExchange#getAttributes() attribute} that
68+
* contains a map with URI variable names and a corresponding MultiValueMap
69+
* of URI matrix variables for each.
6770
* <p>Note: This attribute is not required to be supported by all
6871
* HandlerMapping implementations and may also not be present depending on
6972
* whether the HandlerMapping is configured to keep matrix variable content
@@ -72,8 +75,8 @@ public interface HandlerMapping {
7275
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
7376

7477
/**
75-
* Name of the {@link ServerWebExchange} attribute that contains the set of
76-
* producible MediaTypes applicable to the mapped handler.
78+
* Name of the {@link ServerWebExchange#getAttributes() attribute} containing
79+
* the set of producible MediaType's applicable to the mapped handler.
7780
* <p>Note: This attribute is not required to be supported by all
7881
* HandlerMapping implementations. Handlers should not necessarily expect
7982
* this request attribute to be present in all scenarios.

spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,9 @@ private Object handleMatch(Object handler, PathPattern bestMatch, String pathWit
145145

146146
validateHandler(handler, exchange);
147147

148-
exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
148+
exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
149149
exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatch);
150+
exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
150151

151152
return handler;
152153
}

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ protected HandlerMethod lookupHandlerMethod(String lookupPath, ServerWebExchange
322322
lookupPath + "': {" + m1 + ", " + m2 + "}");
323323
}
324324
}
325-
handleMatch(bestMatch.mapping, lookupPath, exchange);
325+
handleMatch(bestMatch.mapping, bestMatch.handlerMethod, lookupPath, exchange);
326326
return bestMatch.handlerMethod;
327327
}
328328
else {
@@ -342,10 +342,12 @@ private void addMatchingMappings(Collection<T> mappings, List<Match> matches, Se
342342
/**
343343
* Invoked when a matching mapping is found.
344344
* @param mapping the matching mapping
345+
* @param handlerMethod the matching method
345346
* @param lookupPath the lookup path within the current mapping
346347
* @param exchange the current exchange
347348
*/
348-
protected void handleMatch(T mapping, String lookupPath, ServerWebExchange exchange) {
349+
protected void handleMatch(T mapping, HandlerMethod handlerMethod, String lookupPath,
350+
ServerWebExchange exchange) {
349351
}
350352

351353
/**

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,10 @@ protected Comparator<RequestMappingInfo> getMappingComparator(final ServerWebExc
106106
* @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
107107
*/
108108
@Override
109-
protected void handleMatch(RequestMappingInfo info, String lookupPath, ServerWebExchange exchange) {
110-
super.handleMatch(info, lookupPath, exchange);
109+
protected void handleMatch(RequestMappingInfo info, HandlerMethod handlerMethod, String lookupPath,
110+
ServerWebExchange exchange) {
111+
112+
super.handleMatch(info, handlerMethod, lookupPath, exchange);
111113

112114
PathPattern bestPattern;
113115
Map<String, String> uriVariables;
@@ -133,6 +135,7 @@ protected void handleMatch(RequestMappingInfo info, String lookupPath, ServerWeb
133135
));
134136
}
135137

138+
exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handlerMethod);
136139
exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
137140
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables);
138141

spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.springframework.http.MediaType;
4040
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
4141
import org.springframework.stereotype.Controller;
42+
import org.springframework.util.ClassUtils;
4243
import org.springframework.util.MultiValueMap;
4344
import org.springframework.web.bind.annotation.GetMapping;
4445
import org.springframework.web.bind.annotation.PutMapping;
@@ -60,6 +61,7 @@
6061
import static org.junit.Assert.assertEquals;
6162
import static org.junit.Assert.assertNotNull;
6263
import static org.junit.Assert.assertNull;
64+
import static org.junit.Assert.assertSame;
6365
import static org.junit.Assert.assertThat;
6466
import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.get;
6567
import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.method;
@@ -69,6 +71,8 @@
6971
import static org.springframework.web.method.MvcAnnotationPredicates.getMapping;
7072
import static org.springframework.web.method.MvcAnnotationPredicates.requestMapping;
7173
import static org.springframework.web.method.ResolvableMethod.on;
74+
import static org.springframework.web.reactive.HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE;
75+
import static org.springframework.web.reactive.HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE;
7276
import static org.springframework.web.reactive.result.method.RequestMappingInfo.paths;
7377

7478
/**
@@ -77,6 +81,9 @@
7781
*/
7882
public class RequestMappingInfoHandlerMappingTests {
7983

84+
private static final HandlerMethod handlerMethod = new HandlerMethod(new TestController(),
85+
ClassUtils.getMethod(TestController.class, "dummy"));
86+
8087
private TestRequestMappingInfoHandlerMapping handlerMapping;
8188

8289

@@ -169,8 +176,8 @@ public void getHandlerTestInvalidContentType() throws Exception {
169176
Mono<Object> mono = this.handlerMapping.getHandler(exchange);
170177

171178
assertError(mono, UnsupportedMediaTypeStatusException.class,
172-
ex -> assertEquals("Response status 415 with reason \"Invalid mime type \"bogus\": does not contain '/'\"",
173-
ex.getMessage()));
179+
ex -> assertEquals("Response status 415 with reason \"Invalid mime type \"bogus\": " +
180+
"does not contain '/'\"", ex.getMessage()));
174181
}
175182

176183
@Test // SPR-8462
@@ -219,7 +226,7 @@ public void handleMatchUriTemplateVariables() throws Exception {
219226
String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value();
220227

221228
RequestMappingInfo key = paths("/{path1}/{path2}").build();
222-
this.handlerMapping.handleMatch(key, lookupPath, exchange);
229+
this.handlerMapping.handleMatch(key, handlerMethod, lookupPath, exchange);
223230

224231
String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
225232
Map<String, String> uriVariables = (Map<String, String>) exchange.getAttributes().get(name);
@@ -236,7 +243,7 @@ public void handleMatchUriTemplateVariablesDecode() throws Exception {
236243
ServerWebExchange exchange = method(HttpMethod.GET, url).toExchange();
237244

238245
String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value();
239-
this.handlerMapping.handleMatch(key, lookupPath, exchange);
246+
this.handlerMapping.handleMatch(key, handlerMethod, lookupPath, exchange);
240247

241248
String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
242249
@SuppressWarnings("unchecked")
@@ -252,22 +259,23 @@ public void handleMatchBestMatchingPatternAttribute() throws Exception {
252259
RequestMappingInfo key = paths("/{path1}/2", "/**").build();
253260
ServerWebExchange exchange = get("/1/2").toExchange();
254261
String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value();
255-
this.handlerMapping.handleMatch(key, lookupPath, exchange);
262+
this.handlerMapping.handleMatch(key, handlerMethod, lookupPath, exchange);
256263

257-
PathPattern bestMatch = (PathPattern) exchange.getAttributes()
258-
.get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
264+
PathPattern bestMatch = (PathPattern) exchange.getAttributes().get(BEST_MATCHING_PATTERN_ATTRIBUTE);
259265
assertEquals("/{path1}/2", bestMatch.getPatternString());
266+
267+
HandlerMethod mapped = (HandlerMethod) exchange.getAttributes().get(BEST_MATCHING_HANDLER_ATTRIBUTE);
268+
assertSame(handlerMethod, mapped);
260269
}
261270

262271
@Test
263272
public void handleMatchBestMatchingPatternAttributeNoPatternsDefined() throws Exception {
264273
RequestMappingInfo key = paths().build();
265274
ServerWebExchange exchange = get("/1/2").toExchange();
266275
String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value();
267-
this.handlerMapping.handleMatch(key, lookupPath, exchange);
276+
this.handlerMapping.handleMatch(key, handlerMethod, lookupPath, exchange);
268277

269-
PathPattern bestMatch = (PathPattern) exchange.getAttributes()
270-
.get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
278+
PathPattern bestMatch = (PathPattern) exchange.getAttributes().get(BEST_MATCHING_PATTERN_ATTRIBUTE);
271279
assertEquals("/1/2", bestMatch.getPatternString());
272280
}
273281

@@ -330,7 +338,6 @@ private <T> void assertError(Mono<Object> mono, final Class<T> exceptionClass, f
330338
.consumeErrorWith(error -> {
331339
assertEquals(exceptionClass, error.getClass());
332340
consumer.accept((T) error);
333-
334341
})
335342
.verify();
336343
}
@@ -375,19 +382,19 @@ private void testMediaTypeNotAcceptable(String url) throws Exception {
375382
private void handleMatch(ServerWebExchange exchange, String pattern) {
376383
RequestMappingInfo info = paths(pattern).build();
377384
String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value();
378-
this.handlerMapping.handleMatch(info, lookupPath, exchange);
385+
this.handlerMapping.handleMatch(info, handlerMethod, lookupPath, exchange);
379386
}
380387

381388
@SuppressWarnings("unchecked")
382389
private MultiValueMap<String, String> getMatrixVariables(ServerWebExchange exchange, String uriVarName) {
383-
String attrName = HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE;
384-
return ((Map<String, MultiValueMap<String, String>>) exchange.getAttributes().get(attrName)).get(uriVarName);
390+
return ((Map<String, MultiValueMap<String, String>>) exchange.getAttributes()
391+
.get(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE)).get(uriVarName);
385392
}
386393

387394
@SuppressWarnings("unchecked")
388395
private Map<String, String> getUriTemplateVariables(ServerWebExchange exchange) {
389-
String attrName = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
390-
return (Map<String, String>) exchange.getAttributes().get(attrName);
396+
return (Map<String, String>) exchange.getAttributes()
397+
.get(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
391398
}
392399

393400

@@ -446,6 +453,8 @@ public HttpHeaders fooOptions() {
446453
headers.add("Allow", "PUT,POST");
447454
return headers;
448455
}
456+
457+
public void dummy() { }
449458
}
450459

451460

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,11 @@ public interface HandlerMapping {
9696

9797
/**
9898
* Name of the {@link HttpServletRequest} attribute that contains a map with
99-
* URI matrix variables.
99+
* URI variable names and a corresponding MultiValueMap of URI matrix
100+
* variables for each.
100101
* <p>Note: This attribute is not required to be supported by all
101102
* HandlerMapping implementations and may also not be present depending on
102103
* whether the HandlerMapping is configured to keep matrix variable content
103-
* in the request URI.
104104
*/
105105
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
106106

0 commit comments

Comments
 (0)