Browse Source

Reject multiple @⁠HttpExchange declarations in MVC and WebFlux

This commit updates the RequestMappingHandlerMapping implementations in
Spring MVC and Spring WebFlux so that multiple @⁠HttpExchange
declarations on the same element are rejected.

Closes gh-32049
pull/32057/head
Sam Brannen 2 years ago
parent
commit
b8b31ff8a1
  1. 10
      spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java
  2. 57
      spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMappingTests.java
  3. 10
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java
  4. 58
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java

10
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java

@ -200,9 +200,13 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -200,9 +200,13 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
return createRequestMappingInfo(requestMappings.get(0).annotation, customCondition);
}
HttpExchange httpExchange = AnnotatedElementUtils.findMergedAnnotation(element, HttpExchange.class);
if (httpExchange != null) {
return createRequestMappingInfo(httpExchange, customCondition);
List<AnnotationDescriptor<HttpExchange>> httpExchanges = getAnnotationDescriptors(
mergedAnnotations, HttpExchange.class);
if (!httpExchanges.isEmpty()) {
Assert.state(httpExchanges.size() == 1,
() -> "Multiple @HttpExchange annotations found on %s, but only one is allowed: %s"
.formatted(element, httpExchanges));
return createRequestMappingInfo(httpExchanges.get(0).annotation, customCondition);
}
return null;

57
spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMappingTests.java

@ -48,10 +48,12 @@ import org.springframework.web.reactive.result.condition.PatternsRequestConditio @@ -48,10 +48,12 @@ import org.springframework.web.reactive.result.condition.PatternsRequestConditio
import org.springframework.web.reactive.result.method.RequestMappingInfo;
import org.springframework.web.service.annotation.HttpExchange;
import org.springframework.web.service.annotation.PostExchange;
import org.springframework.web.service.annotation.PutExchange;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.Mockito.mock;
/**
@ -154,6 +156,38 @@ class RequestMappingHandlerMappingTests { @@ -154,6 +156,38 @@ class RequestMappingHandlerMappingTests {
assertComposedAnnotationMapping(RequestMethod.PATCH);
}
@Test // gh-32049
void httpExchangeWithMultipleAnnotationsAtClassLevel() throws NoSuchMethodException {
this.handlerMapping.afterPropertiesSet();
Class<?> controllerClass = MultipleClassLevelAnnotationsHttpExchangeController.class;
Method method = controllerClass.getDeclaredMethod("post");
assertThatIllegalStateException()
.isThrownBy(() -> this.handlerMapping.getMappingForMethod(method, controllerClass))
.withMessageContainingAll(
"Multiple @HttpExchange annotations found on " + controllerClass,
"@" + HttpExchange.class.getName(),
"@" + ExtraHttpExchange.class.getName()
);
}
@Test // gh-32049
void httpExchangeWithMultipleAnnotationsAtMethodLevel() throws NoSuchMethodException {
this.handlerMapping.afterPropertiesSet();
Class<?> controllerClass = MultipleMethodLevelAnnotationsHttpExchangeController.class;
Method method = controllerClass.getDeclaredMethod("post");
assertThatIllegalStateException()
.isThrownBy(() -> this.handlerMapping.getMappingForMethod(method, controllerClass))
.withMessageContainingAll(
"Multiple @HttpExchange annotations found on " + method,
"@" + PostExchange.class.getName(),
"@" + PutExchange.class.getName()
);
}
@SuppressWarnings("DataFlowIssue")
@Test
void httpExchangeWithDefaultValues() throws NoSuchMethodException {
@ -313,4 +347,27 @@ class RequestMappingHandlerMappingTests { @@ -313,4 +347,27 @@ class RequestMappingHandlerMappingTests {
public void customValuesExchange(){}
}
@HttpExchange("/exchange")
@ExtraHttpExchange
static class MultipleClassLevelAnnotationsHttpExchangeController {
@PostExchange("/post")
void post() {}
}
static class MultipleMethodLevelAnnotationsHttpExchangeController {
@PostExchange("/post")
@PutExchange("/post")
void post() {}
}
@HttpExchange
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ExtraHttpExchange {
}
}

10
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java

@ -360,9 +360,13 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -360,9 +360,13 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
return createRequestMappingInfo(requestMappings.get(0).annotation, customCondition);
}
HttpExchange httpExchange = AnnotatedElementUtils.findMergedAnnotation(element, HttpExchange.class);
if (httpExchange != null) {
return createRequestMappingInfo(httpExchange, customCondition);
List<AnnotationDescriptor<HttpExchange>> httpExchanges = getAnnotationDescriptors(
mergedAnnotations, HttpExchange.class);
if (!httpExchanges.isEmpty()) {
Assert.state(httpExchanges.size() == 1,
() -> "Multiple @HttpExchange annotations found on %s, but only one is allowed: %s"
.formatted(element, httpExchanges));
return createRequestMappingInfo(httpExchanges.get(0).annotation, customCondition);
}
return null;

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

@ -48,6 +48,7 @@ import org.springframework.web.context.support.StaticWebApplicationContext; @@ -48,6 +48,7 @@ import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.method.HandlerTypePredicate;
import org.springframework.web.service.annotation.HttpExchange;
import org.springframework.web.service.annotation.PostExchange;
import org.springframework.web.service.annotation.PutExchange;
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.condition.MediaTypeExpression;
@ -58,6 +59,7 @@ import org.springframework.web.util.pattern.PathPattern; @@ -58,6 +59,7 @@ import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.Mockito.mock;
/**
@ -276,6 +278,38 @@ class RequestMappingHandlerMappingTests { @@ -276,6 +278,38 @@ class RequestMappingHandlerMappingTests {
assertComposedAnnotationMapping(RequestMethod.PATCH);
}
@Test // gh-32049
void httpExchangeWithMultipleAnnotationsAtClassLevel() throws NoSuchMethodException {
RequestMappingHandlerMapping mapping = createMapping();
Class<?> controllerClass = MultipleClassLevelAnnotationsHttpExchangeController.class;
Method method = controllerClass.getDeclaredMethod("post");
assertThatIllegalStateException()
.isThrownBy(() -> mapping.getMappingForMethod(method, controllerClass))
.withMessageContainingAll(
"Multiple @HttpExchange annotations found on " + controllerClass,
"@" + HttpExchange.class.getName(),
"@" + ExtraHttpExchange.class.getName()
);
}
@Test // gh-32049
void httpExchangeWithMultipleAnnotationsAtMethodLevel() throws NoSuchMethodException {
RequestMappingHandlerMapping mapping = createMapping();
Class<?> controllerClass = MultipleMethodLevelAnnotationsHttpExchangeController.class;
Method method = controllerClass.getDeclaredMethod("post");
assertThatIllegalStateException()
.isThrownBy(() -> mapping.getMappingForMethod(method, controllerClass))
.withMessageContainingAll(
"Multiple @HttpExchange annotations found on " + method,
"@" + PostExchange.class.getName(),
"@" + PutExchange.class.getName()
);
}
@SuppressWarnings("DataFlowIssue")
@Test
void httpExchangeWithDefaultValues() throws NoSuchMethodException {
@ -437,6 +471,30 @@ class RequestMappingHandlerMappingTests { @@ -437,6 +471,30 @@ class RequestMappingHandlerMappingTests {
}
@HttpExchange("/exchange")
@ExtraHttpExchange
static class MultipleClassLevelAnnotationsHttpExchangeController {
@PostExchange("/post")
void post() {}
}
static class MultipleMethodLevelAnnotationsHttpExchangeController {
@PostExchange("/post")
@PutExchange("/post")
void post() {}
}
@HttpExchange
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ExtraHttpExchange {
}
private static class Foo {
}

Loading…
Cancel
Save