From 912fa7602a068ab535b13ae6391d512437b58443 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Mon, 5 Dec 2022 09:19:15 +0000 Subject: [PATCH] Improve invalid Content-Type handling in WebFlux Closes gh-29565 --- .../org/springframework/http/HttpHeaders.java | 3 ++- ...AbstractMessageReaderArgumentResolver.java | 13 ++++++++++++- ...equestBodyMethodArgumentResolverTests.java | 19 +++++++++++++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index a6ac7a4b1090..132e47846a09 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -984,7 +984,8 @@ public void setContentType(@Nullable MediaType mediaType) { /** * Return the {@linkplain MediaType media type} of the body, as specified * by the {@code Content-Type} header. - *

Returns {@code null} when the content-type is unknown. + *

Returns {@code null} when the {@code Content-Type} header is not set. + * @throws InvalidMediaTypeException if the media type value cannot be parsed */ @Nullable public MediaType getContentType() { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java index 5d7129b8fa22..cbc793eb9ebb 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java @@ -35,8 +35,10 @@ import org.springframework.core.codec.Hints; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.http.InvalidMediaTypeException; import org.springframework.http.MediaType; import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -146,7 +148,16 @@ protected Mono readBody(MethodParameter bodyParam, @Nullable MethodParam ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); - MediaType contentType = request.getHeaders().getContentType(); + MediaType contentType; + HttpHeaders headers = request.getHeaders(); + try { + contentType = headers.getContentType(); + } + catch (InvalidMediaTypeException ex) { + throw new UnsupportedMediaTypeStatusException( + "Can't parse Content-Type [" + headers.getFirst("Content-Type") + "]: " + ex.getMessage()); + } + MediaType mediaType = (contentType != null ? contentType : MediaType.APPLICATION_OCTET_STREAM); Object[] hints = extractValidationHints(bodyParam); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyMethodArgumentResolverTests.java index 49f78fa70e5a..2992dac9d6a7 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyMethodArgumentResolverTests.java @@ -41,12 +41,14 @@ import org.springframework.web.reactive.BindingContext; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebInputException; +import org.springframework.web.server.UnsupportedMediaTypeStatusException; import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest; import org.springframework.web.testfixture.method.ResolvableMethod; import org.springframework.web.testfixture.server.MockServerWebExchange; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.springframework.web.testfixture.method.MvcAnnotationPredicates.requestBody; /** @@ -214,6 +216,17 @@ public void emptyBodyWithCompletableFuture() { }); } + @Test // gh-29565 + public void invalidContentType() { + MethodParameter parameter = this.testMethod.annot(requestBody()).arg(String.class); + + ServerWebExchange exchange = MockServerWebExchange.from( + MockServerHttpRequest.post("/path").header("Content-Type", "invalid").build()); + + assertThatThrownBy(() -> this.resolver.readBody(parameter, true, new BindingContext(), exchange)) + .isInstanceOf(UnsupportedMediaTypeStatusException.class); + } + @SuppressWarnings("unchecked") private T resolveValue(MethodParameter param, String body) { ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post("/path").body(body)); @@ -221,7 +234,8 @@ private T resolveValue(MethodParameter param, String body) { Object value = result.block(Duration.ofSeconds(5)); assertThat(value).isNotNull(); - assertThat(param.getParameterType().isAssignableFrom(value.getClass())).as("Unexpected return value type: " + value).isTrue(); + assertThat(param.getParameterType().isAssignableFrom(value.getClass())) + .as("Unexpected return value type: " + value).isTrue(); //no inspection unchecked return (T) value; @@ -234,7 +248,8 @@ private T resolveValueWithEmptyBody(MethodParameter param) { Object value = result.block(Duration.ofSeconds(5)); if (value != null) { - assertThat(param.getParameterType().isAssignableFrom(value.getClass())).as("Unexpected parameter type: " + value).isTrue(); + assertThat(param.getParameterType().isAssignableFrom(value.getClass())) + .as("Unexpected parameter type: " + value).isTrue(); } //no inspection unchecked