Fix review remarks on Servlet.fn
This commit incoporates the remarks made during the Servlet.fn review. See gh-21490
This commit is contained in:
parent
2f682e1035
commit
0e9ea2c94d
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.web.servlet.function;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URI;
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
|
|
@ -39,12 +40,14 @@ import org.reactivestreams.Publisher;
|
|||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.CacheControl;
|
||||
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.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
|
@ -64,7 +67,7 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
|||
|
||||
private final T entity;
|
||||
|
||||
private final BuilderFunction<T> builderFunction;
|
||||
private final Type entityType;
|
||||
|
||||
private int status = HttpStatus.OK.value();
|
||||
|
||||
|
|
@ -73,9 +76,9 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
|||
private final MultiValueMap<String, Cookie> cookies = new LinkedMultiValueMap<>();
|
||||
|
||||
|
||||
private DefaultEntityResponseBuilder(T entity, BuilderFunction<T> builderFunction) {
|
||||
private DefaultEntityResponseBuilder(T entity, @Nullable Type entityType) {
|
||||
this.entity = entity;
|
||||
this.builderFunction = builderFunction;
|
||||
this.entityType = (entityType != null) ? entityType : entity.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -185,9 +188,23 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public EntityResponse<T> build() {
|
||||
return this.builderFunction.build(this.status, this.headers, this.cookies, this.entity);
|
||||
if (this.entity instanceof CompletionStage) {
|
||||
CompletionStage completionStage = (CompletionStage) this.entity;
|
||||
return new CompletionStageEntityResponse(this.status, this.headers, this.cookies,
|
||||
completionStage, this.entityType);
|
||||
}
|
||||
else if (this.entity instanceof Publisher) {
|
||||
Publisher publisher = (Publisher) this.entity;
|
||||
return new PublisherEntityResponse(this.status, this.headers, this.cookies, publisher,
|
||||
this.entityType);
|
||||
}
|
||||
else {
|
||||
return new DefaultEntityResponse<>(this.status, this.headers, this.cookies, this.entity,
|
||||
this.entityType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -195,42 +212,16 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
|||
* Return a new {@link EntityResponse.Builder} from the given object.
|
||||
*/
|
||||
public static <T> EntityResponse.Builder<T> fromObject(T t) {
|
||||
return new DefaultEntityResponseBuilder<>(t, DefaultEntityResponse::new);
|
||||
return new DefaultEntityResponseBuilder<>(t, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link EntityResponse.Builder} from the given completion stage.
|
||||
* Return a new {@link EntityResponse.Builder} from the given object and type reference.
|
||||
*/
|
||||
public static <T> EntityResponse.Builder<CompletionStage<T>> fromCompletionStage(
|
||||
CompletionStage<T> completionStage) {
|
||||
return new DefaultEntityResponseBuilder<>(completionStage,
|
||||
CompletionStageEntityResponse::new);
|
||||
public static <T> EntityResponse.Builder<T> fromObject(T t, ParameterizedTypeReference<?> bodyType) {
|
||||
return new DefaultEntityResponseBuilder<>(t, bodyType.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link EntityResponse.Builder} from the given Reactive Streams publisher.
|
||||
*/
|
||||
public static <T> EntityResponse.Builder<Publisher<T>> fromPublisher(Publisher<T> publisher) {
|
||||
return new DefaultEntityResponseBuilder<>(publisher, PublisherEntityResponse::new);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> HttpMessageConverter<T> cast(HttpMessageConverter<?> messageConverter) {
|
||||
return (HttpMessageConverter<T>) messageConverter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines contract for building {@link EntityResponse} instances.
|
||||
*/
|
||||
private interface BuilderFunction<T> {
|
||||
|
||||
EntityResponse<T> build(int statusCode, HttpHeaders headers,
|
||||
MultiValueMap<String, Cookie> cookies, T entity);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Default {@link EntityResponse} implementation for synchronous bodies.
|
||||
*/
|
||||
|
|
@ -240,12 +231,15 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
|||
|
||||
private final T entity;
|
||||
|
||||
private final Type entityType;
|
||||
|
||||
|
||||
public DefaultEntityResponse(int statusCode, HttpHeaders headers,
|
||||
MultiValueMap<String, Cookie> cookies, T entity) {
|
||||
MultiValueMap<String, Cookie> cookies, T entity, Type entityType) {
|
||||
|
||||
super(statusCode, headers, cookies);
|
||||
this.entity = entity;
|
||||
this.entityType = entityType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -258,11 +252,12 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
|||
HttpServletResponse servletResponse, Context context)
|
||||
throws ServletException, IOException {
|
||||
|
||||
writeEntityWithMessageConverters(this.entity, servletRequest, servletResponse, context);
|
||||
writeEntityWithMessageConverters(this.entity, servletRequest,servletResponse, context);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void writeEntityWithMessageConverters(Object entity,
|
||||
HttpServletRequest request, HttpServletResponse response,
|
||||
ServerResponse.Context context)
|
||||
|
|
@ -271,30 +266,39 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
|||
ServletServerHttpResponse serverResponse = new ServletServerHttpResponse(response);
|
||||
|
||||
MediaType contentType = getContentType(response);
|
||||
Class<?> entityType = entity.getClass();
|
||||
Class<?> entityClass = entity.getClass();
|
||||
|
||||
HttpMessageConverter<Object> messageConverter = context.messageConverters().stream()
|
||||
.filter(converter -> converter.canWrite(entityType, contentType))
|
||||
.findFirst()
|
||||
.map(DefaultEntityResponseBuilder::cast)
|
||||
.orElseThrow(() -> new HttpMediaTypeNotAcceptableException(
|
||||
producibleMediaTypes(context.messageConverters(), entityType)));
|
||||
for (HttpMessageConverter<?> messageConverter : context.messageConverters()) {
|
||||
if (messageConverter instanceof GenericHttpMessageConverter<?>) {
|
||||
GenericHttpMessageConverter<Object> genericMessageConverter =
|
||||
(GenericHttpMessageConverter<Object>) messageConverter;
|
||||
if (genericMessageConverter.canWrite(this.entityType, entityClass, contentType)) {
|
||||
genericMessageConverter.write(entity, this.entityType, contentType, serverResponse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (messageConverter.canWrite(entityClass, contentType)) {
|
||||
((HttpMessageConverter<Object>)messageConverter).write(entity, contentType, serverResponse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
messageConverter.write(entity, contentType, serverResponse);
|
||||
List<MediaType> producibleMediaTypes = producibleMediaTypes(context.messageConverters(), entityClass);
|
||||
throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MediaType getContentType(HttpServletResponse response) {
|
||||
private static MediaType getContentType(HttpServletResponse response) {
|
||||
try {
|
||||
return MediaType.parseMediaType(response.getContentType());
|
||||
return MediaType.parseMediaType(response.getContentType()).removeQualityValue();
|
||||
}
|
||||
catch (InvalidMediaTypeException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected final void tryWriteEntityWithMessageConverters(Object entity,
|
||||
HttpServletRequest request, HttpServletResponse response,
|
||||
protected void tryWriteEntityWithMessageConverters(Object entity,
|
||||
HttpServletRequest request, HttpServletResponse response,
|
||||
ServerResponse.Context context) {
|
||||
try {
|
||||
writeEntityWithMessageConverters(entity, request, response, context);
|
||||
|
|
@ -323,10 +327,10 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
|||
private static class CompletionStageEntityResponse<T>
|
||||
extends DefaultEntityResponse<CompletionStage<T>> {
|
||||
|
||||
public CompletionStageEntityResponse(int statusCode,
|
||||
HttpHeaders headers,
|
||||
MultiValueMap<String, Cookie> cookies, CompletionStage<T> entity) {
|
||||
super(statusCode, headers, cookies, entity);
|
||||
public CompletionStageEntityResponse(int statusCode, HttpHeaders headers,
|
||||
MultiValueMap<String, Cookie> cookies, CompletionStage<T> entity,
|
||||
Type entityType) {
|
||||
super(statusCode, headers, cookies, entity, entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -338,6 +342,7 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
|||
entity().whenComplete((entity, throwable) -> {
|
||||
try {
|
||||
if (entity != null) {
|
||||
|
||||
tryWriteEntityWithMessageConverters(entity,
|
||||
(HttpServletRequest) asyncContext.getRequest(),
|
||||
(HttpServletResponse) asyncContext.getResponse(),
|
||||
|
|
@ -358,8 +363,9 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
|||
private static class PublisherEntityResponse<T> extends DefaultEntityResponse<Publisher<T>> {
|
||||
|
||||
public PublisherEntityResponse(int statusCode, HttpHeaders headers,
|
||||
MultiValueMap<String, Cookie> cookies, Publisher<T> entity) {
|
||||
super(statusCode, headers, cookies, entity);
|
||||
MultiValueMap<String, Cookie> cookies, Publisher<T> entity,
|
||||
Type entityType) {
|
||||
super(statusCode, headers, cookies, entity, entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -425,6 +431,8 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
|
|||
(HttpServletRequest) this.asyncContext.getRequest(),
|
||||
(HttpServletResponse) this.asyncContext.getResponse(),
|
||||
this.context);
|
||||
|
||||
this.asyncContext.complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.web.servlet.function;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
|
|
@ -48,7 +49,6 @@ import org.springframework.http.MediaType;
|
|||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
|
@ -152,35 +152,42 @@ class DefaultServerRequest implements ServerRequest {
|
|||
@Override
|
||||
public <T> T body(ParameterizedTypeReference<T> bodyType) throws IOException, ServletException {
|
||||
Type type = bodyType.getType();
|
||||
Class<?> contextClass = null;
|
||||
return bodyInternal(type, bodyClass(type));
|
||||
}
|
||||
|
||||
static Class<?> bodyClass(Type type) {
|
||||
if (type instanceof Class) {
|
||||
contextClass = (Class<?>) type;
|
||||
return (Class<?>) type;
|
||||
}
|
||||
return bodyInternal(type, contextClass);
|
||||
if (type instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
if (parameterizedType.getRawType() instanceof Class) {
|
||||
return (Class<?>) parameterizedType.getRawType();
|
||||
}
|
||||
}
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T bodyInternal(Type type, @Nullable Class<?> contextClass)
|
||||
private <T> T bodyInternal(Type bodyType, Class<?> bodyClass)
|
||||
throws ServletException, IOException {
|
||||
|
||||
MediaType contentType =
|
||||
this.headers.contentType().orElse(MediaType.APPLICATION_OCTET_STREAM);
|
||||
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
if (messageConverter instanceof GenericHttpMessageConverter<?>) {
|
||||
if (messageConverter instanceof GenericHttpMessageConverter) {
|
||||
GenericHttpMessageConverter<T> genericMessageConverter =
|
||||
(GenericHttpMessageConverter<T>) messageConverter;
|
||||
if (genericMessageConverter.canRead(type, contextClass, contentType)) {
|
||||
return genericMessageConverter.read(type, contextClass, this.serverHttpRequest);
|
||||
if (genericMessageConverter.canRead(bodyType, bodyClass, contentType)) {
|
||||
return genericMessageConverter.read(bodyType, bodyClass, this.serverHttpRequest);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (messageConverter.canRead(contextClass, contentType)) {
|
||||
HttpMessageConverter<T> theConverter =
|
||||
(HttpMessageConverter<T>) messageConverter;
|
||||
Class<? extends T> clazz = (Class<? extends T>) contextClass;
|
||||
return theConverter.read(clazz, this.serverHttpRequest);
|
||||
}
|
||||
if (messageConverter.canRead(bodyClass, contentType)) {
|
||||
HttpMessageConverter<T> theConverter =
|
||||
(HttpMessageConverter<T>) messageConverter;
|
||||
Class<? extends T> clazz = (Class<? extends T>) bodyClass;
|
||||
return theConverter.read(clazz, this.serverHttpRequest);
|
||||
}
|
||||
}
|
||||
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
|
||||
|
|
@ -196,6 +203,11 @@ class DefaultServerRequest implements ServerRequest {
|
|||
return this.attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> param(String name) {
|
||||
return Optional.ofNullable(servletRequest().getParameter(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValueMap<String, String> params() {
|
||||
return this.params;
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ import org.springframework.http.HttpMethod;
|
|||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
|
@ -239,35 +238,29 @@ class DefaultServerRequestBuilder implements ServerRequest.Builder {
|
|||
@Override
|
||||
public <T> T body(ParameterizedTypeReference<T> bodyType) throws IOException, ServletException {
|
||||
Type type = bodyType.getType();
|
||||
Class<?> contextClass = null;
|
||||
if (type instanceof Class) {
|
||||
contextClass = (Class<?>) type;
|
||||
}
|
||||
return bodyInternal(type, contextClass);
|
||||
return bodyInternal(type, DefaultServerRequest.bodyClass(type));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T bodyInternal(Type type, @Nullable Class<?> contextClass)
|
||||
private <T> T bodyInternal(Type bodyType, Class<?> bodyClass)
|
||||
throws ServletException, IOException {
|
||||
|
||||
HttpInputMessage inputMessage = new BuiltInputMessage();
|
||||
MediaType contentType = headers().contentType().orElse(MediaType.APPLICATION_OCTET_STREAM);
|
||||
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
if (messageConverter instanceof GenericHttpMessageConverter<?>) {
|
||||
if (messageConverter instanceof GenericHttpMessageConverter) {
|
||||
GenericHttpMessageConverter<T> genericMessageConverter =
|
||||
(GenericHttpMessageConverter<T>) messageConverter;
|
||||
if (genericMessageConverter.canRead(type, contextClass, contentType)) {
|
||||
return genericMessageConverter.read(type, contextClass, inputMessage);
|
||||
if (genericMessageConverter.canRead(bodyType, bodyClass, contentType)) {
|
||||
return genericMessageConverter.read(bodyType, bodyClass, inputMessage);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (messageConverter.canRead(contextClass, contentType)) {
|
||||
HttpMessageConverter<T> theConverter =
|
||||
(HttpMessageConverter<T>) messageConverter;
|
||||
Class<? extends T> clazz = (Class<? extends T>) contextClass;
|
||||
return theConverter.read(clazz, inputMessage);
|
||||
}
|
||||
if (messageConverter.canRead(bodyClass, contentType)) {
|
||||
HttpMessageConverter<T> theConverter =
|
||||
(HttpMessageConverter<T>) messageConverter;
|
||||
Class<? extends T> clazz = (Class<? extends T>) bodyClass;
|
||||
return theConverter.read(clazz, inputMessage);
|
||||
}
|
||||
}
|
||||
throw new HttpMediaTypeNotSupportedException(contentType, Collections.emptyList());
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
|
@ -38,8 +37,7 @@ import javax.servlet.http.Cookie;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
|
@ -196,16 +194,8 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ServerResponse asyncBody(CompletionStage<?> asyncBody) {
|
||||
return DefaultEntityResponseBuilder.fromCompletionStage(asyncBody)
|
||||
.headers(this.headers)
|
||||
.status(this.statusCode)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerResponse asyncBody(Publisher<?> futureBody) {
|
||||
return DefaultEntityResponseBuilder.fromPublisher(futureBody)
|
||||
public <T> ServerResponse body(T body, ParameterizedTypeReference<T> bodyType) {
|
||||
return DefaultEntityResponseBuilder.fromObject(body, bodyType)
|
||||
.headers(this.headers)
|
||||
.status(this.statusCode)
|
||||
.build();
|
||||
|
|
|
|||
|
|
@ -20,12 +20,10 @@ import java.net.URI;
|
|||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.function.Consumer;
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
|
@ -61,24 +59,16 @@ public interface EntityResponse<T> extends ServerResponse {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a builder for an asynchronous body supplied by the given {@link CompletionStage}.
|
||||
* @param completionStage the supplier of the response body
|
||||
* @param <T> the type of the elements contained in the publisher
|
||||
* Create a builder with the given object and type reference.
|
||||
* @param t the object that represents the body of the response
|
||||
* @param entityType the type of the entity, used to capture the generic type
|
||||
* @param <T> the type of element contained in the publisher
|
||||
* @return the created builder
|
||||
*/
|
||||
static <T> Builder<CompletionStage<T>> fromCompletionStage(CompletionStage<T> completionStage) {
|
||||
return DefaultEntityResponseBuilder.fromCompletionStage(completionStage);
|
||||
static <T> Builder<T> fromObject(T t, ParameterizedTypeReference<T> entityType) {
|
||||
return DefaultEntityResponseBuilder.fromObject(t, entityType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a builder for an asynchronous body supplied by the given {@link Publisher}.
|
||||
* @param publisher the supplier of the response body
|
||||
* @param <T> the type of the elements contained in the publisher
|
||||
* @return the created builder
|
||||
*/
|
||||
static <T> Builder<Publisher<T>> fromPublisher(Publisher<T> publisher) {
|
||||
return DefaultEntityResponseBuilder.fromPublisher(publisher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a builder for {@code EntityResponse}.
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
|
@ -362,26 +363,23 @@ public interface ServerResponse {
|
|||
|
||||
/**
|
||||
* Set the body of the response to the given {@code Object} and return it.
|
||||
*
|
||||
* <p>Asynchronous response bodies are supported by providing a {@link CompletionStage} or
|
||||
* {@link Publisher} as body.
|
||||
* @param body the body of the response
|
||||
* @return the built response
|
||||
*/
|
||||
ServerResponse body(Object body);
|
||||
|
||||
/**
|
||||
* Set the asynchronous body of the response to the given {@link CompletionStage} and
|
||||
* return it.
|
||||
* @param asyncBody the body of the response
|
||||
* Set the body of the response to the given {@code Object} and return it. The parameter
|
||||
* {@code bodyType} is used to capture the generic type.
|
||||
*
|
||||
* @param body the body of the response
|
||||
* @param bodyType the type of the body, used to capture the generic type
|
||||
* @return the built response
|
||||
*/
|
||||
ServerResponse asyncBody(CompletionStage<?> asyncBody);
|
||||
|
||||
/**
|
||||
* Set the asynchronous body of the response to the given {@link Publisher} and
|
||||
* return it.
|
||||
* @param asyncBody the body of the response
|
||||
* @return the built response
|
||||
*/
|
||||
ServerResponse asyncBody(Publisher<?> asyncBody);
|
||||
<T> ServerResponse body(T body, ParameterizedTypeReference<T> bodyType);
|
||||
|
||||
/**
|
||||
* Render the template with the given {@code name} using the given {@code modelAttributes}.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import javax.servlet.http.Cookie;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
|
@ -63,6 +64,16 @@ public class DefaultEntityResponseBuilderTests {
|
|||
assertSame(body, response.entity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromObjectTypeReference() {
|
||||
String body = "foo";
|
||||
EntityResponse<String> response = EntityResponse.fromObject(body,
|
||||
new ParameterizedTypeReference<String>() {})
|
||||
.build();
|
||||
|
||||
assertSame(body, response.entity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void status() {
|
||||
String body = "foo";
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import org.springframework.http.HttpRange;
|
|||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.test.MockHttpSession;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
|
@ -240,14 +241,16 @@ public class DefaultServerRequestTests {
|
|||
@Test
|
||||
public void bodyParameterizedTypeReference() throws Exception {
|
||||
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
|
||||
servletRequest.setContentType(MediaType.TEXT_PLAIN_VALUE);
|
||||
servletRequest.setContent("foo".getBytes(UTF_8));
|
||||
servletRequest.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
servletRequest.setContent("[\"foo\",\"bar\"]".getBytes(UTF_8));
|
||||
|
||||
DefaultServerRequest request = new DefaultServerRequest(servletRequest,
|
||||
this.messageConverters);
|
||||
Collections.singletonList(new MappingJackson2HttpMessageConverter()));
|
||||
|
||||
String result = request.body(new ParameterizedTypeReference<String>() {});
|
||||
assertEquals("foo", result);
|
||||
List<String> result = request.body(new ParameterizedTypeReference<List<String>>() {});
|
||||
assertEquals(2, result.size());
|
||||
assertEquals("foo", result.get(0));
|
||||
assertEquals("bar", result.get(1));
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.net.URI;
|
|||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
|
@ -31,6 +32,7 @@ import org.junit.Test;
|
|||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
|
@ -38,6 +40,7 @@ import org.springframework.http.HttpStatus;
|
|||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.test.MockHttpServletResponse;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
|
@ -288,10 +291,27 @@ public class DefaultServerResponseBuilderTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void asyncBodyCompletionStage() throws Exception {
|
||||
public void bodyWithParameterizedTypeReference() throws Exception {
|
||||
List<String> body = new ArrayList<>();
|
||||
body.add("foo");
|
||||
body.add("bar");
|
||||
ServerResponse response = ServerResponse.ok().body(body, new ParameterizedTypeReference<List<String>>() {});
|
||||
|
||||
MockHttpServletRequest mockRequest = new MockHttpServletRequest("GET", "http://example.com");
|
||||
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||
ServerResponse.Context context = () -> Collections.singletonList(new MappingJackson2HttpMessageConverter());
|
||||
|
||||
ModelAndView mav = response.writeTo(mockRequest, mockResponse, context);
|
||||
assertNull(mav);
|
||||
|
||||
assertEquals("[\"foo\",\"bar\"]", mockResponse.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bodyCompletionStage() throws Exception {
|
||||
String body = "foo";
|
||||
CompletionStage<String> completionStage = CompletableFuture.completedFuture(body);
|
||||
ServerResponse response = ServerResponse.ok().asyncBody(completionStage);
|
||||
ServerResponse response = ServerResponse.ok().body(completionStage);
|
||||
|
||||
MockHttpServletRequest mockRequest = new MockHttpServletRequest("GET", "http://example.com");
|
||||
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||
|
|
@ -307,10 +327,10 @@ public class DefaultServerResponseBuilderTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void asyncBodyPublisher() throws Exception {
|
||||
public void bodyPublisher() throws Exception {
|
||||
String body = "foo";
|
||||
Publisher<String> publisher = Mono.just(body);
|
||||
ServerResponse response = ServerResponse.ok().asyncBody(publisher);
|
||||
ServerResponse response = ServerResponse.ok().body(publisher);
|
||||
|
||||
MockHttpServletRequest mockRequest = new MockHttpServletRequest("GET", "http://example.com");
|
||||
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||
|
|
|
|||
Loading…
Reference in New Issue