Moved BodyExtractor and BodyInserter to http.codec
This commit moves the web.reactive.function.[BodyInserter|BodyExtractor] to http.codec, so that they can be used from the client as well. Furthermore, it parameterized both inserter and extractor over ReactiveHttpOutputMessage and ReactiveHttpInputMessage respectively, so that they can be limited to only be used on the client or server.
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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.function;
|
||||
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
|
||||
/**
|
||||
* A function that can extract data from a {@link Request} body.
|
||||
*
|
||||
* @param <T> the type of data to extract
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
* @see Request#body(BodyExtractor)
|
||||
* @see BodyExtractors
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface BodyExtractor<T> {
|
||||
|
||||
/**
|
||||
* Extract from the given request.
|
||||
* @param request the request to extract from
|
||||
* @param strategies the strategies to use
|
||||
* @return the extracted data
|
||||
*/
|
||||
T extract(ServerHttpRequest request, StrategiesSupplier strategies);
|
||||
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* 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.function;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
||||
|
||||
/**
|
||||
* Implementations of {@link BodyExtractor} that read various bodies, such a reactive streams.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class BodyExtractors {
|
||||
|
||||
/**
|
||||
* Return a {@code BodyExtractor} that reads into a Reactor {@link Mono}.
|
||||
* @param elementClass the class of element in the {@code Mono}
|
||||
* @param <T> the element type
|
||||
* @return a {@code BodyExtractor} that reads a mono
|
||||
*/
|
||||
public static <T> BodyExtractor<Mono<T>> toMono(Class<? extends T> elementClass) {
|
||||
Assert.notNull(elementClass, "'elementClass' must not be null");
|
||||
return toMono(ResolvableType.forClass(elementClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@code BodyExtractor} that reads into a Reactor {@link Mono}.
|
||||
* @param elementType the type of element in the {@code Mono}
|
||||
* @param <T> the element type
|
||||
* @return a {@code BodyExtractor} that reads a mono
|
||||
*/
|
||||
public static <T> BodyExtractor<Mono<T>> toMono(ResolvableType elementType) {
|
||||
Assert.notNull(elementType, "'elementType' must not be null");
|
||||
return (request, strategies) -> readWithMessageReaders(request, strategies,
|
||||
elementType,
|
||||
reader -> reader.readMono(elementType, request, Collections.emptyMap()),
|
||||
Mono::error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@code BodyExtractor} that reads into a Reactor {@link Flux}.
|
||||
* @param elementClass the class of element in the {@code Flux}
|
||||
* @param <T> the element type
|
||||
* @return a {@code BodyExtractor} that reads a mono
|
||||
*/
|
||||
public static <T> BodyExtractor<Flux<T>> toFlux(Class<? extends T> elementClass) {
|
||||
Assert.notNull(elementClass, "'elementClass' must not be null");
|
||||
return toFlux(ResolvableType.forClass(elementClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@code BodyExtractor} that reads into a Reactor {@link Flux}.
|
||||
* @param elementType the type of element in the {@code Flux}
|
||||
* @param <T> the element type
|
||||
* @return a {@code BodyExtractor} that reads a mono
|
||||
*/
|
||||
public static <T> BodyExtractor<Flux<T>> toFlux(ResolvableType elementType) {
|
||||
Assert.notNull(elementType, "'elementType' must not be null");
|
||||
return (request, strategies) -> readWithMessageReaders(request, strategies,
|
||||
elementType,
|
||||
reader -> reader.read(elementType, request, Collections.emptyMap()),
|
||||
Flux::error);
|
||||
}
|
||||
|
||||
private static <T, S extends Publisher<T>> S readWithMessageReaders(
|
||||
ServerHttpRequest request,
|
||||
StrategiesSupplier strategies,
|
||||
ResolvableType elementType,
|
||||
Function<HttpMessageReader<T>, S> readerFunction,
|
||||
Function<Throwable, S> unsupportedError) {
|
||||
|
||||
MediaType contentType = contentType(request);
|
||||
Supplier<Stream<HttpMessageReader<?>>> messageReaders = strategies.messageReaders();
|
||||
return messageReaders.get()
|
||||
.filter(r -> r.canRead(elementType, contentType))
|
||||
.findFirst()
|
||||
.map(BodyExtractors::<T>cast)
|
||||
.map(readerFunction)
|
||||
.orElseGet(() -> {
|
||||
List<MediaType> supportedMediaTypes = messageReaders.get()
|
||||
.flatMap(reader -> reader.getReadableMediaTypes().stream())
|
||||
.collect(Collectors.toList());
|
||||
UnsupportedMediaTypeStatusException error =
|
||||
new UnsupportedMediaTypeStatusException(contentType, supportedMediaTypes);
|
||||
return unsupportedError.apply(error);
|
||||
});
|
||||
}
|
||||
|
||||
private static MediaType contentType(ServerHttpRequest request) {
|
||||
MediaType result = request.getHeaders().getContentType();
|
||||
return result != null ? result : MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> HttpMessageReader<T> cast(HttpMessageReader<?> messageReader) {
|
||||
return (HttpMessageReader<T>) messageReader;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* 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.function;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A component that can insert data into a {@link Response} body.
|
||||
*
|
||||
* @param <T> the type of data to insert
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
* @see Response#body()
|
||||
* @see Response.BodyBuilder#body(BodyInserter)
|
||||
* @see BodyInserters
|
||||
*/
|
||||
public interface BodyInserter<T> {
|
||||
|
||||
/**
|
||||
* Insert into the given response.
|
||||
* @param response the response to insert into
|
||||
* @param strategies the strategies to use
|
||||
* @return a {@code Mono} that indicates completion or error
|
||||
*/
|
||||
Mono<Void> insert(ServerHttpResponse response, StrategiesSupplier strategies);
|
||||
|
||||
/**
|
||||
* Return the type contained in the body.
|
||||
* @return the type contained in the body
|
||||
*/
|
||||
T t();
|
||||
|
||||
|
||||
/**
|
||||
* Return a new {@code BodyInserter} described by the given writer and supplier functions.
|
||||
* @param writer the writer function for the new inserter
|
||||
* @param supplier the supplier function for the new inserter
|
||||
* @param <T> the type supplied and written by the inserter
|
||||
* @return the new {@code BodyInserter}
|
||||
*/
|
||||
static <T> BodyInserter<T> of(BiFunction<ServerHttpResponse, StrategiesSupplier, Mono<Void>> writer,
|
||||
Supplier<T> supplier) {
|
||||
|
||||
Assert.notNull(writer, "'writer' must not be null");
|
||||
Assert.notNull(supplier, "'supplier' must not be null");
|
||||
|
||||
return new BodyInserters.DefaultBodyInserter<T>(writer, supplier);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
/*
|
||||
* 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.function;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.codec.ResourceHttpMessageWriter;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.http.codec.ServerSentEventHttpMessageWriter;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Implementations of {@link BodyInserter} that write various bodies, such a reactive streams,
|
||||
* server-sent events, resources, etc.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class BodyInserters {
|
||||
|
||||
private static final ResolvableType RESOURCE_TYPE = ResolvableType.forClass(Resource.class);
|
||||
|
||||
private static final ResolvableType SERVER_SIDE_EVENT_TYPE =
|
||||
ResolvableType.forClass(ServerSentEvent.class);
|
||||
|
||||
private static final boolean jackson2Present =
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
|
||||
BodyInserters.class.getClassLoader()) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
|
||||
BodyInserters.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* Return a {@code BodyInserter} that writes the given single object.
|
||||
* @param body the body of the response
|
||||
* @return a {@code BodyInserter} that writes a single object
|
||||
*/
|
||||
public static <T> BodyInserter<T> fromObject(T body) {
|
||||
Assert.notNull(body, "'body' must not be null");
|
||||
return BodyInserter.of(
|
||||
(response, strategies) -> writeWithMessageWriters(response, strategies,
|
||||
Mono.just(body), ResolvableType.forInstance(body)),
|
||||
() -> body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@code BodyInserter} that writes the given {@link Publisher}.
|
||||
* @param publisher the publisher to stream to the response body
|
||||
* @param elementClass the class of elements contained in the publisher
|
||||
* @param <T> the type of the elements contained in the publisher
|
||||
* @param <S> the type of the {@code Publisher}.
|
||||
* @return a {@code BodyInserter} that writes a {@code Publisher}
|
||||
*/
|
||||
public static <S extends Publisher<T>, T> BodyInserter<S> fromPublisher(S publisher,
|
||||
Class<T> elementClass) {
|
||||
|
||||
Assert.notNull(publisher, "'publisher' must not be null");
|
||||
Assert.notNull(elementClass, "'elementClass' must not be null");
|
||||
return fromPublisher(publisher, ResolvableType.forClass(elementClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@code BodyInserter} that writes the given {@link Publisher}.
|
||||
* @param publisher the publisher to stream to the response body
|
||||
* @param elementType the type of elements contained in the publisher
|
||||
* @param <T> the type of the elements contained in the publisher
|
||||
* @param <S> the type of the {@code Publisher}.
|
||||
* @return a {@code BodyInserter} that writes a {@code Publisher}
|
||||
*/
|
||||
public static <S extends Publisher<T>, T> BodyInserter<S> fromPublisher(S publisher,
|
||||
ResolvableType elementType) {
|
||||
|
||||
Assert.notNull(publisher, "'publisher' must not be null");
|
||||
Assert.notNull(elementType, "'elementType' must not be null");
|
||||
return BodyInserter.of(
|
||||
(response, strategies) -> writeWithMessageWriters(response, strategies,
|
||||
publisher, elementType),
|
||||
() -> publisher
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@code BodyInserter} that writes the given {@code Resource}.
|
||||
* If the resource can be resolved to a {@linkplain Resource#getFile() file}, it will be copied
|
||||
* using
|
||||
* <a href="https://en.wikipedia.org/wiki/Zero-copy">zero-copy</a>
|
||||
* @param resource the resource to write to the response
|
||||
* @param <T> the type of the {@code Resource}
|
||||
* @return a {@code BodyInserter} that writes a {@code Publisher}
|
||||
*/
|
||||
public static <T extends Resource> BodyInserter<T> fromResource(T resource) {
|
||||
Assert.notNull(resource, "'resource' must not be null");
|
||||
return BodyInserter.of(
|
||||
(response, strategies) -> {
|
||||
ResourceHttpMessageWriter messageWriter = new ResourceHttpMessageWriter();
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
return messageWriter.write(Mono.just(resource), RESOURCE_TYPE, contentType,
|
||||
response, Collections.emptyMap());
|
||||
},
|
||||
() -> resource
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@code BodyInserter} that writes the given {@code ServerSentEvent} publisher.
|
||||
* @param eventsPublisher the {@code ServerSentEvent} publisher to write to the response body
|
||||
* @param <T> the type of the elements contained in the {@link ServerSentEvent}
|
||||
* @return a {@code BodyInserter} that writes a {@code ServerSentEvent} publisher
|
||||
* @see <a href="https://www.w3.org/TR/eventsource/">Server-Sent Events W3C recommendation</a>
|
||||
*/
|
||||
public static <T, S extends Publisher<ServerSentEvent<T>>> BodyInserter<S> fromServerSentEvents(
|
||||
S eventsPublisher) {
|
||||
|
||||
Assert.notNull(eventsPublisher, "'eventsPublisher' must not be null");
|
||||
return BodyInserter.of(
|
||||
(response, strategies) -> {
|
||||
ServerSentEventHttpMessageWriter messageWriter = sseMessageWriter();
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
return messageWriter.write(eventsPublisher, SERVER_SIDE_EVENT_TYPE,
|
||||
contentType, response, Collections.emptyMap());
|
||||
},
|
||||
() -> eventsPublisher
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@code BodyInserter} that writes the given {@code Publisher} publisher as
|
||||
* Server-Sent Events.
|
||||
* @param eventsPublisher the publisher to write to the response body as Server-Sent Events
|
||||
* @param eventClass the class of event contained in the publisher
|
||||
* @param <T> the type of the elements contained in the publisher
|
||||
* @return a {@code BodyInserter} that writes the given {@code Publisher} publisher as
|
||||
* Server-Sent Events
|
||||
* @see <a href="https://www.w3.org/TR/eventsource/">Server-Sent Events W3C recommendation</a>
|
||||
*/
|
||||
public static <T, S extends Publisher<T>> BodyInserter<S> fromServerSentEvents(S eventsPublisher,
|
||||
Class<T> eventClass) {
|
||||
|
||||
Assert.notNull(eventsPublisher, "'eventsPublisher' must not be null");
|
||||
Assert.notNull(eventClass, "'eventClass' must not be null");
|
||||
return fromServerSentEvents(eventsPublisher, ResolvableType.forClass(eventClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@code BodyInserter} that writes the given {@code Publisher} publisher as
|
||||
* Server-Sent Events.
|
||||
* @param eventsPublisher the publisher to write to the response body as Server-Sent Events
|
||||
* @param eventType the type of event contained in the publisher
|
||||
* @param <T> the type of the elements contained in the publisher
|
||||
* @return a {@code BodyInserter} that writes the given {@code Publisher} publisher as
|
||||
* Server-Sent Events
|
||||
* @see <a href="https://www.w3.org/TR/eventsource/">Server-Sent Events W3C recommendation</a>
|
||||
*/
|
||||
public static <T, S extends Publisher<T>> BodyInserter<S> fromServerSentEvents(S eventsPublisher,
|
||||
ResolvableType eventType) {
|
||||
|
||||
Assert.notNull(eventsPublisher, "'eventsPublisher' must not be null");
|
||||
Assert.notNull(eventType, "'eventType' must not be null");
|
||||
return BodyInserter.of(
|
||||
(response, strategies) -> {
|
||||
ServerSentEventHttpMessageWriter messageWriter = sseMessageWriter();
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
return messageWriter.write(eventsPublisher, eventType, contentType, response,
|
||||
Collections.emptyMap());
|
||||
|
||||
},
|
||||
() -> eventsPublisher
|
||||
);
|
||||
}
|
||||
|
||||
private static ServerSentEventHttpMessageWriter sseMessageWriter() {
|
||||
return jackson2Present ? new ServerSentEventHttpMessageWriter(
|
||||
Collections.singletonList(new Jackson2JsonEncoder())) :
|
||||
new ServerSentEventHttpMessageWriter();
|
||||
}
|
||||
|
||||
private static <T> Mono<Void> writeWithMessageWriters(ServerHttpResponse response,
|
||||
StrategiesSupplier strategies,
|
||||
Publisher<T> body,
|
||||
ResolvableType bodyType) {
|
||||
|
||||
// TODO: use ContentNegotiatingResultHandlerSupport
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
return strategies.messageWriters().get()
|
||||
.filter(messageWriter -> messageWriter.canWrite(bodyType, contentType))
|
||||
.findFirst()
|
||||
.map(BodyInserters::cast)
|
||||
.map(messageWriter -> messageWriter
|
||||
.write(body, bodyType, contentType, response, Collections
|
||||
.emptyMap()))
|
||||
.orElseGet(() -> {
|
||||
response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);
|
||||
return response.setComplete();
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> HttpMessageWriter<T> cast(HttpMessageWriter<?> messageWriter) {
|
||||
return (HttpMessageWriter<T>) messageWriter;
|
||||
}
|
||||
|
||||
static class DefaultBodyInserter<T> implements BodyInserter<T> {
|
||||
|
||||
private final BiFunction<ServerHttpResponse, StrategiesSupplier, Mono<Void>> writer;
|
||||
|
||||
private final Supplier<T> supplier;
|
||||
|
||||
public DefaultBodyInserter(
|
||||
BiFunction<ServerHttpResponse, StrategiesSupplier, Mono<Void>> writer,
|
||||
Supplier<T> supplier) {
|
||||
this.writer = writer;
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> insert(ServerHttpResponse response, StrategiesSupplier strategies) {
|
||||
return this.writer.apply(response, strategies);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T t() {
|
||||
return this.supplier.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -24,11 +24,15 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRange;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.BodyExtractor;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@@ -68,8 +72,14 @@ class DefaultRequest implements Request {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T body(BodyExtractor<T> extractor) {
|
||||
return extractor.extract(request(), this.strategies);
|
||||
public <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor) {
|
||||
return extractor.extract(request(),
|
||||
new BodyExtractor.Context() {
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() {
|
||||
return DefaultRequest.this.strategies.messageReaders();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,6 +41,8 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.BodyInserter;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -142,7 +144,7 @@ class DefaultResponseBuilder implements Response.BodyBuilder {
|
||||
@Override
|
||||
public Response<Void> build() {
|
||||
return body(BodyInserter.of(
|
||||
(response, strategies) -> response.setComplete(),
|
||||
(response, context) -> response.setComplete(),
|
||||
() -> null));
|
||||
}
|
||||
|
||||
@@ -150,18 +152,18 @@ class DefaultResponseBuilder implements Response.BodyBuilder {
|
||||
public <T extends Publisher<Void>> Response<T> build(T voidPublisher) {
|
||||
Assert.notNull(voidPublisher, "'voidPublisher' must not be null");
|
||||
return body(BodyInserter.of(
|
||||
(response, strategies) -> Flux.from(voidPublisher).then(response.setComplete()),
|
||||
(response, context) -> Flux.from(voidPublisher).then(response.setComplete()),
|
||||
() -> null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Response<T> body(BiFunction<ServerHttpResponse, StrategiesSupplier, Mono<Void>> writer,
|
||||
public <T> Response<T> body(BiFunction<ServerHttpResponse, BodyInserter.Context, Mono<Void>> writer,
|
||||
Supplier<T> supplier) {
|
||||
return body(BodyInserter.of(writer, supplier));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Response<T> body(BodyInserter<T> inserter) {
|
||||
public <T> Response<T> body(BodyInserter<T, ? super ServerHttpResponse> inserter) {
|
||||
Assert.notNull(inserter, "'inserter' must not be null");
|
||||
return new BodyInserterResponse<T>(this.statusCode, this.headers, inserter);
|
||||
}
|
||||
@@ -235,11 +237,12 @@ class DefaultResponseBuilder implements Response.BodyBuilder {
|
||||
|
||||
private static final class BodyInserterResponse<T> extends AbstractResponse<T> {
|
||||
|
||||
private final BodyInserter<T> inserter;
|
||||
private final BodyInserter<T, ? super ServerHttpResponse> inserter;
|
||||
|
||||
|
||||
public BodyInserterResponse(
|
||||
int statusCode, HttpHeaders headers, BodyInserter<T> inserter) {
|
||||
public BodyInserterResponse(int statusCode, HttpHeaders headers,
|
||||
BodyInserter<T, ? super ServerHttpResponse> inserter) {
|
||||
|
||||
super(statusCode, headers);
|
||||
this.inserter = inserter;
|
||||
}
|
||||
@@ -253,7 +256,12 @@ class DefaultResponseBuilder implements Response.BodyBuilder {
|
||||
public Mono<Void> writeTo(ServerWebExchange exchange, StrategiesSupplier strategies) {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
writeStatusAndHeaders(response);
|
||||
return this.inserter.insert(response, strategies);
|
||||
return this.inserter.insert(response, new BodyInserter.Context() {
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() {
|
||||
return strategies.messageWriters();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRange;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.BodyExtractor;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
|
||||
/**
|
||||
* Represents an HTTP request, as handled by a {@code HandlerFunction}.
|
||||
@@ -67,7 +69,7 @@ public interface Request {
|
||||
* @param <T> the type of the body returned
|
||||
* @return the extracted body
|
||||
*/
|
||||
<T> T body(BodyExtractor<T> extractor);
|
||||
<T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor);
|
||||
|
||||
/**
|
||||
* Return the request attribute value if present.
|
||||
|
||||
@@ -27,6 +27,8 @@ import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.BodyExtractor;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.PathMatcher;
|
||||
@@ -314,7 +316,7 @@ public abstract class RequestPredicates {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T body(BodyExtractor<T> extractor) {
|
||||
public <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor) {
|
||||
return this.request.body(extractor);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.BodyInserter;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -316,7 +317,7 @@ public interface Response<T> {
|
||||
* @param <T> the type contained in the body
|
||||
* @return the built response
|
||||
*/
|
||||
<T> Response<T> body(BiFunction<ServerHttpResponse, StrategiesSupplier, Mono<Void>> writer,
|
||||
<T> Response<T> body(BiFunction<ServerHttpResponse, BodyInserter.Context, Mono<Void>> writer,
|
||||
Supplier<T> supplier);
|
||||
|
||||
/**
|
||||
@@ -325,7 +326,7 @@ public interface Response<T> {
|
||||
* @param <T> the type contained in the body
|
||||
* @return the built response
|
||||
*/
|
||||
<T> Response<T> body(BodyInserter<T> inserter);
|
||||
<T> Response<T> body(BodyInserter<T, ? super ServerHttpResponse> inserter);
|
||||
|
||||
/**
|
||||
* Render the template with the given {@code name} using the given {@code modelAttributes}.
|
||||
|
||||
@@ -28,8 +28,9 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRange;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.BodyExtractor;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.BodyExtractor;
|
||||
import org.springframework.web.reactive.function.HandlerFunction;
|
||||
import org.springframework.web.reactive.function.Request;
|
||||
|
||||
@@ -83,7 +84,7 @@ public class RequestWrapper implements Request {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T body(BodyExtractor<T> extractor) {
|
||||
public <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor) {
|
||||
return this.request.body(extractor);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* 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.function;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.tests.TestSubscriber;
|
||||
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class BodyExtractorsTests {
|
||||
|
||||
@Test
|
||||
public void toMono() throws Exception {
|
||||
BodyExtractor<Mono<String>> extractor = BodyExtractors.toMono(String.class);
|
||||
|
||||
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
|
||||
DefaultDataBuffer dataBuffer =
|
||||
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
|
||||
Flux<DataBuffer> body = Flux.just(dataBuffer);
|
||||
|
||||
MockServerHttpRequest request = new MockServerHttpRequest();
|
||||
request.setBody(body);
|
||||
|
||||
StrategiesSupplier strategies = StrategiesSupplier.builder().build();
|
||||
|
||||
Mono<String> result = extractor.extract(request, strategies);
|
||||
|
||||
TestSubscriber.subscribe(result)
|
||||
.assertComplete()
|
||||
.assertValues("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toFlux() throws Exception {
|
||||
BodyExtractor<Flux<String>> extractor = BodyExtractors.toFlux(String.class);
|
||||
|
||||
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
|
||||
DefaultDataBuffer dataBuffer =
|
||||
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
|
||||
Flux<DataBuffer> body = Flux.just(dataBuffer);
|
||||
|
||||
MockServerHttpRequest request = new MockServerHttpRequest();
|
||||
request.setBody(body);
|
||||
|
||||
StrategiesSupplier strategies = StrategiesSupplier.builder().build();
|
||||
|
||||
Flux<String> result = extractor.extract(request, strategies);
|
||||
TestSubscriber.subscribe(result)
|
||||
.assertComplete()
|
||||
.assertValues("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toFluxUnacceptable() throws Exception {
|
||||
BodyExtractor<Flux<String>> extractor = BodyExtractors.toFlux(String.class);
|
||||
|
||||
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
|
||||
DefaultDataBuffer dataBuffer =
|
||||
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
|
||||
Flux<DataBuffer> body = Flux.just(dataBuffer);
|
||||
|
||||
MockServerHttpRequest request = new MockServerHttpRequest();
|
||||
request.getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||
request.setBody(body);
|
||||
|
||||
StrategiesSupplier strategies = StrategiesSupplier.empty().build();
|
||||
|
||||
Flux<String> result = extractor.extract(request, strategies);
|
||||
TestSubscriber.subscribe(result)
|
||||
.assertError(UnsupportedMediaTypeStatusException.class);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* 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.function;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
||||
import org.springframework.tests.TestSubscriber;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class BodyInsertersTests {
|
||||
|
||||
@Test
|
||||
public void ofObject() throws Exception {
|
||||
String body = "foo";
|
||||
BodyInserter<String> inserter = BodyInserters.fromObject(body);
|
||||
|
||||
assertEquals(body, inserter.t());
|
||||
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
Mono<Void> result = inserter.insert(response, StrategiesSupplier.builder().build());
|
||||
TestSubscriber.subscribe(result)
|
||||
.assertComplete();
|
||||
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(body.getBytes(UTF_8));
|
||||
DataBuffer buffer = new DefaultDataBufferFactory().wrap(byteBuffer);
|
||||
TestSubscriber.subscribe(response.getBody())
|
||||
.assertComplete()
|
||||
.assertValues(buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofPublisher() throws Exception {
|
||||
Flux<String> body = Flux.just("foo");
|
||||
BodyInserter<Flux<String>> inserter = BodyInserters.fromPublisher(body, String.class);
|
||||
|
||||
assertEquals(body, inserter.t());
|
||||
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
Mono<Void> result = inserter.insert(response, StrategiesSupplier.builder().build());
|
||||
TestSubscriber.subscribe(result)
|
||||
.assertComplete();
|
||||
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap("foo".getBytes(UTF_8));
|
||||
DataBuffer buffer = new DefaultDataBufferFactory().wrap(byteBuffer);
|
||||
TestSubscriber.subscribe(response.getBody())
|
||||
.assertComplete()
|
||||
.assertValues(buffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofResource() throws Exception {
|
||||
Resource body = new ClassPathResource("response.txt", getClass());
|
||||
BodyInserter<Resource> inserter = BodyInserters.fromResource(body);
|
||||
|
||||
assertEquals(body, inserter.t());
|
||||
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
Mono<Void> result = inserter.insert(response, StrategiesSupplier.builder().build());
|
||||
TestSubscriber.subscribe(result)
|
||||
.assertComplete();
|
||||
|
||||
byte[] expectedBytes = Files.readAllBytes(body.getFile().toPath());
|
||||
|
||||
TestSubscriber.subscribe(response.getBody())
|
||||
.assertComplete()
|
||||
.assertValuesWith(dataBuffer -> {
|
||||
byte[] resultBytes = new byte[dataBuffer.readableByteCount()];
|
||||
dataBuffer.read(resultBytes);
|
||||
assertArrayEquals(expectedBytes, resultBytes);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofServerSentEventFlux() throws Exception {
|
||||
ServerSentEvent<String> event = ServerSentEvent.builder("foo").build();
|
||||
Flux<ServerSentEvent<String>> body = Flux.just(event);
|
||||
BodyInserter<Flux<ServerSentEvent<String>>> inserter =
|
||||
BodyInserters.fromServerSentEvents(body);
|
||||
|
||||
assertEquals(body, inserter.t());
|
||||
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
Mono<Void> result = inserter.insert(response, StrategiesSupplier.builder().build());
|
||||
TestSubscriber.subscribe(result)
|
||||
.assertComplete();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofServerSentEventClass() throws Exception {
|
||||
Flux<String> body = Flux.just("foo");
|
||||
BodyInserter<Flux<String>> inserter =
|
||||
BodyInserters.fromServerSentEvents(body, String.class);
|
||||
|
||||
assertEquals(body, inserter.t());
|
||||
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
Mono<Void> result = inserter.insert(response, StrategiesSupplier.builder().build());
|
||||
TestSubscriber.subscribe(result)
|
||||
.assertComplete();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,7 +52,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.web.reactive.function.BodyExtractors.toMono;
|
||||
import static org.springframework.http.codec.BodyExtractors.toMono;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.BodyInserter;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
@@ -217,7 +218,7 @@ public class DefaultResponseBuilderTests {
|
||||
public void bodyInserter() throws Exception {
|
||||
String body = "foo";
|
||||
Supplier<String> supplier = () -> body;
|
||||
BiFunction<ServerHttpResponse, StrategiesSupplier, Mono<Void>> writer =
|
||||
BiFunction<ServerHttpResponse, BodyInserter.Context, Mono<Void>> writer =
|
||||
(response, strategies) -> {
|
||||
byte[] bodyBytes = body.getBytes(UTF_8);
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(bodyBytes);
|
||||
|
||||
@@ -50,6 +50,7 @@ import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.http.codec.BodyInserters.fromPublisher;
|
||||
import static org.springframework.web.reactive.function.RouterFunctions.route;
|
||||
|
||||
/**
|
||||
@@ -155,14 +156,14 @@ public class DispatcherHandlerIntegrationTests extends AbstractHttpHandlerIntegr
|
||||
|
||||
public Response<Publisher<Person>> mono(Request request) {
|
||||
Person person = new Person("John");
|
||||
return Response.ok().body(BodyInserters.fromPublisher(Mono.just(person), Person.class));
|
||||
return Response.ok().body(fromPublisher(Mono.just(person), Person.class));
|
||||
}
|
||||
|
||||
public Response<Publisher<Person>> flux(Request request) {
|
||||
Person person1 = new Person("John");
|
||||
Person person2 = new Person("Jane");
|
||||
return Response.ok().body(
|
||||
BodyInserters.fromPublisher(Flux.just(person1, person2), Person.class));
|
||||
fromPublisher(Flux.just(person1, person2), Person.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRange;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.BodyExtractor;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -88,9 +90,9 @@ public class MockRequest<T> implements Request {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <S> S body(BodyExtractor<S> extractor) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S> S body(BodyExtractor<S, ? super ServerHttpRequest> extractor){
|
||||
return (S) this.body;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.web.reactive.function.BodyExtractors.toMono;
|
||||
import static org.springframework.web.reactive.function.BodyInserters.fromPublisher;
|
||||
import static org.springframework.http.codec.BodyExtractors.toMono;
|
||||
import static org.springframework.http.codec.BodyInserters.fromPublisher;
|
||||
import static org.springframework.web.reactive.function.RequestPredicates.GET;
|
||||
import static org.springframework.web.reactive.function.RequestPredicates.POST;
|
||||
import static org.springframework.web.reactive.function.RouterFunctions.route;
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
|
||||
import static org.springframework.http.codec.BodyInserters.fromObject;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.tests.TestSubscriber;
|
||||
import org.springframework.web.client.reactive.WebClient;
|
||||
|
||||
import static org.springframework.http.codec.BodyInserters.fromServerSentEvents;
|
||||
import static org.springframework.web.client.reactive.ClientWebRequestBuilders.get;
|
||||
import static org.springframework.web.client.reactive.ResponseExtractors.bodyStream;
|
||||
import static org.springframework.web.reactive.function.RouterFunctions.route;
|
||||
@@ -111,13 +112,13 @@ public class SseHandlerFunctionIntegrationTests
|
||||
|
||||
public Response<Publisher<String>> string(Request request) {
|
||||
Flux<String> flux = Flux.interval(Duration.ofMillis(100)).map(l -> "foo " + l).take(2);
|
||||
return Response.ok().body(BodyInserters.fromServerSentEvents(flux, String.class));
|
||||
return Response.ok().body(fromServerSentEvents(flux, String.class));
|
||||
}
|
||||
|
||||
public Response<Publisher<Person>> person(Request request) {
|
||||
Flux<Person> flux = Flux.interval(Duration.ofMillis(100))
|
||||
.map(l -> new Person("foo " + l)).take(2);
|
||||
return Response.ok().body(BodyInserters.fromServerSentEvents(flux, Person.class));
|
||||
return Response.ok().body(fromServerSentEvents(flux, Person.class));
|
||||
}
|
||||
|
||||
public Response<Publisher<ServerSentEvent<String>>> sse(Request request) {
|
||||
@@ -127,7 +128,7 @@ public class SseHandlerFunctionIntegrationTests
|
||||
.comment("bar")
|
||||
.build()).take(2);
|
||||
|
||||
return Response.ok().body(BodyInserters.fromServerSentEvents(flux));
|
||||
return Response.ok().body(fromServerSentEvents(flux));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Hello World
|
||||
This is a sample response text file.
|
||||
Reference in New Issue
Block a user