diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java index 21e0623bffe..a131a8f28d1 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.codec.DecodingException; import org.springframework.core.codec.Hints; import org.springframework.http.HttpCookie; import org.springframework.http.HttpHeaders; @@ -48,6 +49,7 @@ import org.springframework.web.reactive.function.BodyExtractor; import org.springframework.web.reactive.function.BodyExtractors; import org.springframework.web.reactive.function.UnsupportedMediaTypeException; import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebInputException; import org.springframework.web.server.UnsupportedMediaTypeStatusException; import org.springframework.web.server.WebSession; import org.springframework.web.util.UriBuilder; @@ -67,6 +69,9 @@ class DefaultServerRequest implements ServerRequest { ex.getContentType(), ex.getSupportedMediaTypes(), ex.getBodyType()) : new UnsupportedMediaTypeStatusException(ex.getMessage())); + private static final Function DECODING_MAPPER = + ex -> new ServerWebInputException("Failed to read HTTP message", null, ex); + private final ServerWebExchange exchange; @@ -154,25 +159,29 @@ class DefaultServerRequest implements ServerRequest { @Override public Mono bodyToMono(Class elementClass) { Mono mono = body(BodyExtractors.toMono(elementClass)); - return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER); + return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER) + .onErrorMap(DecodingException.class, DECODING_MAPPER); } @Override public Mono bodyToMono(ParameterizedTypeReference typeReference) { Mono mono = body(BodyExtractors.toMono(typeReference)); - return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER); + return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER) + .onErrorMap(DecodingException.class, DECODING_MAPPER); } @Override public Flux bodyToFlux(Class elementClass) { Flux flux = body(BodyExtractors.toFlux(elementClass)); - return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER); + return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER) + .onErrorMap(DecodingException.class, DECODING_MAPPER); } @Override public Flux bodyToFlux(ParameterizedTypeReference typeReference) { Flux flux = body(BodyExtractors.toFlux(typeReference)); - return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER); + return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER) + .onErrorMap(DecodingException.class, DECODING_MAPPER); } @Override diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerRequestTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerRequestTests.java index 3f5b853b259..cc189ccdfca 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerRequestTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerRequestTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -45,12 +46,14 @@ import org.springframework.http.HttpRange; import org.springframework.http.MediaType; import org.springframework.http.codec.DecoderHttpMessageReader; import org.springframework.http.codec.HttpMessageReader; +import org.springframework.http.codec.json.Jackson2JsonDecoder; import org.springframework.http.codec.multipart.FormFieldPart; import org.springframework.http.codec.multipart.Part; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.mock.web.test.server.MockServerWebExchange; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebInputException; import org.springframework.web.server.UnsupportedMediaTypeStatusException; import static org.junit.Assert.*; @@ -61,7 +64,8 @@ import static org.springframework.web.reactive.function.BodyExtractors.*; */ public class DefaultServerRequestTests { - private final List> messageReaders = Collections.singletonList( + private final List> messageReaders = Arrays.asList( + new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()), new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes())); @@ -279,6 +283,28 @@ public class DefaultServerRequestTests { assertEquals("foo", resultMono.block()); } + @Test + public void bodyToMonoDecodingException() { + DefaultDataBufferFactory factory = new DefaultDataBufferFactory(); + DefaultDataBuffer dataBuffer = + factory.wrap(ByteBuffer.wrap("{\"invalid\":\"json\" ".getBytes(StandardCharsets.UTF_8))); + Flux body = Flux.just(dataBuffer); + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_JSON); + MockServerHttpRequest mockRequest = MockServerHttpRequest + .method(HttpMethod.POST, "http://example.com/invalid") + .headers(httpHeaders) + .body(body); + DefaultServerRequest request = new DefaultServerRequest(MockServerWebExchange.from(mockRequest), messageReaders); + + Mono> resultMono = request.bodyToMono( + new ParameterizedTypeReference>() {}); + StepVerifier.create(resultMono) + .expectError(ServerWebInputException.class) + .verify(); + } + @Test public void bodyToFlux() { DefaultDataBufferFactory factory = new DefaultDataBufferFactory();