Add support for ResponseEntity result handling
This commit is contained in:
parent
59b7c25003
commit
9aa6f5caac
|
|
@ -60,6 +60,7 @@ import org.springframework.web.reactive.result.method.HandlerMethodArgumentResol
|
|||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler;
|
||||
import org.springframework.web.reactive.result.method.annotation.ResponseEntityResultHandler;
|
||||
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
|
||||
|
|
@ -335,13 +336,20 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
|||
}
|
||||
|
||||
@Bean
|
||||
public ResponseBodyResultHandler responseBodyResultHandler() {
|
||||
return new ResponseBodyResultHandler(getMessageConverters(), mvcConversionService());
|
||||
public SimpleResultHandler simpleResultHandler() {
|
||||
return new SimpleResultHandler(mvcConversionService());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpleResultHandler simpleResultHandler() {
|
||||
return new SimpleResultHandler(mvcConversionService());
|
||||
public ResponseEntityResultHandler responseEntityResultHandler() {
|
||||
return new ResponseEntityResultHandler(getMessageConverters(), mvcConversionService(),
|
||||
mvcContentTypeResolver());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResponseBodyResultHandler responseBodyResultHandler() {
|
||||
return new ResponseBodyResultHandler(getMessageConverters(), mvcConversionService(),
|
||||
mvcContentTypeResolver());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||
import org.springframework.web.reactive.result.ContentNegotiatingResultHandlerSupport;
|
||||
import org.springframework.web.server.NotAcceptableStatusException;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* Abstract base class for result handlers that handle return values by writing
|
||||
* to the response with {@link HttpMessageConverter}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public abstract class AbstractMessageConverterResultHandler extends ContentNegotiatingResultHandlerSupport {
|
||||
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor with message converters, a {@code ConversionService}, and a
|
||||
* {@code RequestedContentTypeResolver}.
|
||||
*
|
||||
* @param converters converters for writing the response body with
|
||||
* @param conversionService for converting other reactive types (e.g.
|
||||
* rx.Observable, rx.Single, etc.) to Flux or Mono
|
||||
* @param contentTypeResolver for resolving the requested content type
|
||||
*/
|
||||
protected AbstractMessageConverterResultHandler(List<HttpMessageConverter<?>> converters,
|
||||
ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) {
|
||||
|
||||
super(conversionService, contentTypeResolver);
|
||||
Assert.notEmpty(converters, "At least one message converter is required.");
|
||||
this.messageConverters = converters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured message converters.
|
||||
*/
|
||||
public List<HttpMessageConverter<?>> getMessageConverters() {
|
||||
return this.messageConverters;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Mono<Void> writeBody(ServerWebExchange exchange, Object body, ResolvableType bodyType) {
|
||||
|
||||
Publisher<?> publisher;
|
||||
ResolvableType elementType;
|
||||
|
||||
if (getConversionService().canConvert(bodyType.getRawClass(), Publisher.class)) {
|
||||
if (body != null) {
|
||||
publisher = getConversionService().convert(body, Publisher.class);
|
||||
}
|
||||
else {
|
||||
publisher = Mono.empty();
|
||||
}
|
||||
elementType = bodyType.getGeneric(0);
|
||||
if (Void.class.equals(elementType.getRawClass())) {
|
||||
return Mono.from((Publisher<Void>) publisher);
|
||||
}
|
||||
}
|
||||
else {
|
||||
publisher = Mono.justOrEmpty(body);
|
||||
elementType = bodyType;
|
||||
}
|
||||
|
||||
List<MediaType> producibleTypes = getProducibleMediaTypes(elementType);
|
||||
MediaType bestMediaType = selectMediaType(exchange, producibleTypes);
|
||||
|
||||
if (bestMediaType != null) {
|
||||
for (HttpMessageConverter<?> converter : getMessageConverters()) {
|
||||
if (converter.canWrite(elementType, bestMediaType)) {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
return converter.write((Publisher) publisher, elementType, bestMediaType, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Mono.error(new NotAcceptableStatusException(producibleTypes));
|
||||
}
|
||||
|
||||
private List<MediaType> getProducibleMediaTypes(ResolvableType elementType) {
|
||||
return getMessageConverters().stream()
|
||||
.filter(converter -> converter.canWrite(elementType, null))
|
||||
.flatMap(converter -> converter.getWritableMediaTypes().stream())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -17,29 +17,20 @@
|
|||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.HandlerResultHandler;
|
||||
import org.springframework.web.reactive.accept.HeaderContentTypeResolver;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||
import org.springframework.web.reactive.result.ContentNegotiatingResultHandlerSupport;
|
||||
import org.springframework.web.server.NotAcceptableStatusException;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
|
||||
|
|
@ -48,21 +39,18 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
* with {@code @ResponseBody} writing to the body of the request or response with
|
||||
* an {@link HttpMessageConverter}.
|
||||
*
|
||||
* <p>By default the order for the result handler is set to 0. It is generally
|
||||
* safe and expected it will be ordered ahead of other result handlers since it
|
||||
* only gets involved based on the presence of an {@code @ResponseBody}
|
||||
* annotation.
|
||||
* <p>By default the order for the result handler is set to 100. It detects the
|
||||
* presence of an {@code @ResponseBody} annotation and should be ordered after
|
||||
* result handlers that look for a specific return type such as
|
||||
* {@code ResponseEntity}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Stephane Maldini
|
||||
* @author Sebastien Deleuze
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ResponseBodyResultHandler extends ContentNegotiatingResultHandlerSupport
|
||||
implements HandlerResultHandler, Ordered {
|
||||
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
|
||||
public class ResponseBodyResultHandler extends AbstractMessageConverterResultHandler
|
||||
implements HandlerResultHandler {
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a {@code ConversionService} only
|
||||
|
|
@ -90,20 +78,11 @@ public class ResponseBodyResultHandler extends ContentNegotiatingResultHandlerSu
|
|||
public ResponseBodyResultHandler(List<HttpMessageConverter<?>> converters,
|
||||
ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) {
|
||||
|
||||
super(conversionService, contentTypeResolver);
|
||||
Assert.notEmpty(converters, "At least one message converter is required.");
|
||||
this.messageConverters = converters;
|
||||
setOrder(0);
|
||||
super(converters, conversionService, contentTypeResolver);
|
||||
setOrder(100);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the configured message converters.
|
||||
*/
|
||||
public List<HttpMessageConverter<?>> getMessageConverters() {
|
||||
return this.messageConverters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(HandlerResult result) {
|
||||
Object handler = result.getHandler();
|
||||
|
|
@ -117,51 +96,10 @@ public class ResponseBodyResultHandler extends ContentNegotiatingResultHandlerSu
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
|
||||
|
||||
Publisher<?> publisher;
|
||||
ResolvableType elementType;
|
||||
ResolvableType returnType = result.getReturnValueType();
|
||||
|
||||
if (getConversionService().canConvert(returnType.getRawClass(), Publisher.class)) {
|
||||
Optional<Object> optionalValue = result.getReturnValue();
|
||||
if (optionalValue.isPresent()) {
|
||||
publisher = getConversionService().convert(optionalValue.get(), Publisher.class);
|
||||
}
|
||||
else {
|
||||
publisher = Mono.empty();
|
||||
}
|
||||
elementType = returnType.getGeneric(0);
|
||||
if (Void.class.equals(elementType.getRawClass())) {
|
||||
return Mono.from((Publisher<Void>)publisher);
|
||||
}
|
||||
}
|
||||
else {
|
||||
publisher = Mono.justOrEmpty(result.getReturnValue());
|
||||
elementType = returnType;
|
||||
}
|
||||
|
||||
List<MediaType> producibleTypes = getProducibleMediaTypes(elementType);
|
||||
MediaType bestMediaType = selectMediaType(exchange, producibleTypes);
|
||||
|
||||
if (bestMediaType != null) {
|
||||
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||
if (converter.canWrite(elementType, bestMediaType)) {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
return converter.write((Publisher) publisher, elementType, bestMediaType, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Mono.error(new NotAcceptableStatusException(producibleTypes));
|
||||
}
|
||||
|
||||
private List<MediaType> getProducibleMediaTypes(ResolvableType type) {
|
||||
return this.messageConverters.stream()
|
||||
.filter(converter -> converter.canWrite(type, null))
|
||||
.flatMap(converter -> converter.getWritableMediaTypes().stream())
|
||||
.collect(Collectors.toList());
|
||||
Object body = result.getReturnValue().orElse(null);
|
||||
ResolvableType bodyType = result.getReturnValueType();
|
||||
return writeBody(exchange, body, bodyType);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.HandlerResultHandler;
|
||||
import org.springframework.web.reactive.accept.HeaderContentTypeResolver;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* Handles {@link HttpEntity} and {@link ResponseEntity} return values.
|
||||
*
|
||||
* <p>By default the order for this result handler is set to 0. It is generally
|
||||
* safe to place it early in the order as it looks for a concrete return type.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ResponseEntityResultHandler extends AbstractMessageConverterResultHandler
|
||||
implements HandlerResultHandler {
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a {@code ConversionService} only
|
||||
* and creating a {@link HeaderContentTypeResolver}, i.e. using Accept header
|
||||
* to determine the requested content type.
|
||||
*
|
||||
* @param converters converters for writing the response body with
|
||||
* @param conversionService for converting to Flux and Mono from other reactive types
|
||||
*/
|
||||
public ResponseEntityResultHandler(List<HttpMessageConverter<?>> converters,
|
||||
ConversionService conversionService) {
|
||||
|
||||
this(converters, conversionService, new HeaderContentTypeResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with message converters, a {@code ConversionService}, and a
|
||||
* {@code RequestedContentTypeResolver}.
|
||||
*
|
||||
* @param converters converters for writing the response body with
|
||||
* @param conversionService for converting other reactive types (e.g.
|
||||
* rx.Observable, rx.Single, etc.) to Flux or Mono
|
||||
* @param contentTypeResolver for resolving the requested content type
|
||||
*/
|
||||
public ResponseEntityResultHandler(List<HttpMessageConverter<?>> converters,
|
||||
ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) {
|
||||
|
||||
super(converters, conversionService, contentTypeResolver);
|
||||
setOrder(0);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(HandlerResult result) {
|
||||
ResolvableType returnType = result.getReturnValueType();
|
||||
return (HttpEntity.class.isAssignableFrom(returnType.getRawClass()) &&
|
||||
!RequestEntity.class.isAssignableFrom(returnType.getRawClass()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
|
||||
|
||||
Object body = null;
|
||||
|
||||
Optional<Object> optional = result.getReturnValue();
|
||||
if (optional.isPresent()) {
|
||||
Assert.isInstanceOf(HttpEntity.class, optional.get());
|
||||
HttpEntity<?> httpEntity = (HttpEntity<?>) optional.get();
|
||||
|
||||
if (httpEntity instanceof ResponseEntity) {
|
||||
ResponseEntity<?> responseEntity = (ResponseEntity<?>) httpEntity;
|
||||
exchange.getResponse().setStatusCode(responseEntity.getStatusCode());
|
||||
}
|
||||
|
||||
HttpHeaders entityHeaders = httpEntity.getHeaders();
|
||||
HttpHeaders responseHeaders = exchange.getResponse().getHeaders();
|
||||
|
||||
if (!entityHeaders.isEmpty()) {
|
||||
entityHeaders.entrySet().stream()
|
||||
.filter(entry -> responseHeaders.containsKey(entry.getKey()))
|
||||
.forEach(entry -> responseHeaders.put(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
|
||||
body = httpEntity.getBody();
|
||||
}
|
||||
|
||||
ResolvableType bodyType = result.getReturnValueType().getGeneric(0);
|
||||
return writeBody(exchange, body, bodyType);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -55,6 +55,7 @@ import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
|||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler;
|
||||
import org.springframework.web.reactive.result.method.annotation.ResponseEntityResultHandler;
|
||||
import org.springframework.web.reactive.result.view.HttpMessageConverterView;
|
||||
import org.springframework.web.reactive.result.view.View;
|
||||
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler;
|
||||
|
|
@ -183,13 +184,12 @@ public class WebReactiveConfigurationTests {
|
|||
service.canConvert(Observable.class, Flux.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void responseBodyResultHandler() throws Exception {
|
||||
public void responseEntityResultHandler() throws Exception {
|
||||
ApplicationContext context = loadConfig(WebReactiveConfiguration.class);
|
||||
|
||||
String name = "responseBodyResultHandler";
|
||||
ResponseBodyResultHandler handler = context.getBean(name, ResponseBodyResultHandler.class);
|
||||
String name = "responseEntityResultHandler";
|
||||
ResponseEntityResultHandler handler = context.getBean(name, ResponseEntityResultHandler.class);
|
||||
assertNotNull(handler);
|
||||
|
||||
assertEquals(0, handler.getOrder());
|
||||
|
|
@ -202,6 +202,34 @@ public class WebReactiveConfigurationTests {
|
|||
assertHasConverter(converters, Resource.class, MediaType.IMAGE_PNG);
|
||||
assertHasConverter(converters, TestBean.class, MediaType.APPLICATION_XML);
|
||||
assertHasConverter(converters, TestBean.class, MediaType.APPLICATION_JSON);
|
||||
|
||||
name = "mvcContentTypeResolver";
|
||||
RequestedContentTypeResolver resolver = context.getBean(name, RequestedContentTypeResolver.class);
|
||||
assertSame(resolver, handler.getContentTypeResolver());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseBodyResultHandler() throws Exception {
|
||||
ApplicationContext context = loadConfig(WebReactiveConfiguration.class);
|
||||
|
||||
String name = "responseBodyResultHandler";
|
||||
ResponseBodyResultHandler handler = context.getBean(name, ResponseBodyResultHandler.class);
|
||||
assertNotNull(handler);
|
||||
|
||||
assertEquals(100, handler.getOrder());
|
||||
|
||||
List<HttpMessageConverter<?>> converters = handler.getMessageConverters();
|
||||
assertEquals(5, converters.size());
|
||||
|
||||
assertHasConverter(converters, ByteBuffer.class, MediaType.APPLICATION_OCTET_STREAM);
|
||||
assertHasConverter(converters, String.class, MediaType.TEXT_PLAIN);
|
||||
assertHasConverter(converters, Resource.class, MediaType.IMAGE_PNG);
|
||||
assertHasConverter(converters, TestBean.class, MediaType.APPLICATION_XML);
|
||||
assertHasConverter(converters, TestBean.class, MediaType.APPLICATION_JSON);
|
||||
|
||||
name = "mvcContentTypeResolver";
|
||||
RequestedContentTypeResolver resolver = context.getBean(name, RequestedContentTypeResolver.class);
|
||||
assertSame(resolver, handler.getContentTypeResolver());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -172,6 +172,12 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
|
|||
serializeAsPojo("http://localhost:" + port + "/completable-future");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // Issue #119
|
||||
public void serializeAsMonoResponseEntity() throws Exception {
|
||||
serializeAsPojo("http://localhost:" + port + "/monoResponseEntity");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAsMono() throws Exception {
|
||||
serializeAsPojo("http://localhost:" + port + "/mono");
|
||||
|
|
@ -450,6 +456,12 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
|
|||
return Observable.just(ByteBuffer.wrap("Hello!".getBytes()));
|
||||
}
|
||||
|
||||
@RequestMapping("/monoResponseEntity")
|
||||
public ResponseEntity<Mono<Person>> monoResponseEntity() {
|
||||
Mono<Person> body = Mono.just(new Person("Robert"));
|
||||
return ResponseEntity.ok(body);
|
||||
}
|
||||
|
||||
@RequestMapping("/mono")
|
||||
public Mono<Person> monoResponseBody() {
|
||||
return Mono.just(new Person("Robert"));
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -87,20 +88,19 @@ public class ResponseBodyResultHandlerTests {
|
|||
@Test
|
||||
public void defaultOrder() throws Exception {
|
||||
ResponseBodyResultHandler handler = createHandler(new StringEncoder());
|
||||
assertEquals(0, handler.getOrder());
|
||||
assertEquals(100, handler.getOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesContentTypeResolver() throws Exception {
|
||||
MediaType contentType = MediaType.APPLICATION_JSON_UTF8;
|
||||
RequestedContentTypeResolver resolver = new FixedContentTypeResolver(contentType);
|
||||
RequestedContentTypeResolver resolver = new FixedContentTypeResolver(APPLICATION_JSON_UTF8);
|
||||
HandlerResultHandler handler = createHandler(resolver, new StringEncoder(), new JacksonJsonEncoder());
|
||||
|
||||
ServerWebExchange exchange = createExchange("/foo");
|
||||
HandlerResult result = new HandlerResult(new Object(), "fooValue", ResolvableType.forClass(String.class));
|
||||
handler.handleResult(exchange, result).block();
|
||||
|
||||
assertEquals(contentType, exchange.getResponse().getHeaders().getContentType());
|
||||
assertEquals(APPLICATION_JSON_UTF8, exchange.getResponse().getHeaders().getContentType());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.test.TestSubscriber;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.support.JacksonJsonEncoder;
|
||||
import org.springframework.core.codec.support.StringEncoder;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.HandlerResultHandler;
|
||||
import org.springframework.web.reactive.accept.FixedContentTypeResolver;
|
||||
import org.springframework.web.reactive.accept.HeaderContentTypeResolver;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ResponseEntityResultHandler}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ResponseEntityResultHandlerTests {
|
||||
|
||||
private MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
|
||||
|
||||
@Test
|
||||
public void supports() throws NoSuchMethodException {
|
||||
ResponseEntityResultHandler handler = createHandler(new StringEncoder());
|
||||
TestController controller = new TestController();
|
||||
|
||||
HandlerMethod hm = new HandlerMethod(controller, TestController.class.getMethod("responseString"));
|
||||
ResolvableType type = ResolvableType.forMethodParameter(hm.getReturnType());
|
||||
assertTrue(handler.supports(new HandlerResult(hm, null, type, new ExtendedModelMap())));
|
||||
|
||||
hm = new HandlerMethod(controller, TestController.class.getMethod("responseVoid"));
|
||||
type = ResolvableType.forMethodParameter(hm.getReturnType());
|
||||
assertTrue(handler.supports(new HandlerResult(hm, null, type, new ExtendedModelMap())));
|
||||
|
||||
hm = new HandlerMethod(controller, TestController.class.getMethod("string"));
|
||||
type = ResolvableType.forMethodParameter(hm.getReturnType());
|
||||
assertFalse(handler.supports(new HandlerResult(hm, null, type, new ExtendedModelMap())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultOrder() throws Exception {
|
||||
ResponseEntityResultHandler handler = createHandler(new StringEncoder());
|
||||
assertEquals(0, handler.getOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonResponseBody() throws Exception {
|
||||
RequestedContentTypeResolver resolver = new FixedContentTypeResolver(APPLICATION_JSON_UTF8);
|
||||
HandlerResultHandler handler = createHandler(resolver, new StringEncoder(), new JacksonJsonEncoder());
|
||||
|
||||
TestController controller = new TestController();
|
||||
HandlerMethod hm = new HandlerMethod(controller, controller.getClass().getMethod("responseString"));
|
||||
ResolvableType type = ResolvableType.forMethodParameter(hm.getReturnType());
|
||||
HandlerResult result = new HandlerResult(hm, ResponseEntity.ok("fooValue"), type);
|
||||
|
||||
ServerWebExchange exchange = createExchange("/foo");
|
||||
handler.handleResult(exchange, result).block();
|
||||
|
||||
assertEquals(HttpStatus.OK, this.response.getStatus());
|
||||
assertEquals(APPLICATION_JSON_UTF8, this.response.getHeaders().getContentType());
|
||||
TestSubscriber.subscribe(this.response.getBody())
|
||||
.assertValuesWith(buf -> assertEquals("\"fooValue\"",
|
||||
DataBufferTestUtils.dumpString(buf, Charset.forName("UTF-8"))));
|
||||
}
|
||||
|
||||
|
||||
private ResponseEntityResultHandler createHandler(Encoder<?>... encoders) {
|
||||
return createHandler(new HeaderContentTypeResolver(), encoders);
|
||||
}
|
||||
|
||||
private ResponseEntityResultHandler createHandler(RequestedContentTypeResolver resolver,
|
||||
Encoder<?>... encoders) {
|
||||
|
||||
List<HttpMessageConverter<?>> converters = Arrays.stream(encoders)
|
||||
.map(encoder -> new CodecHttpMessageConverter<>(encoder, null))
|
||||
.collect(Collectors.toList());
|
||||
return new ResponseEntityResultHandler(converters, new DefaultConversionService(), resolver);
|
||||
}
|
||||
|
||||
private ServerWebExchange createExchange(String path) throws URISyntaxException {
|
||||
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI(path));
|
||||
WebSessionManager sessionManager = mock(WebSessionManager.class);
|
||||
return new DefaultServerWebExchange(request, this.response, sessionManager);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestController {
|
||||
|
||||
public ResponseEntity<String> responseString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ResponseEntity<Void> responseVoid() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String string() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue