parent
1a2ac8ea56
commit
101220bad1
|
|
@ -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.core;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Contract for adapting to and from {@link Flux} and {@link Mono}.
|
||||
*
|
||||
* <p>An adapter supports a specific adaptee type whose stream semantics can be
|
||||
* checked via {@link #getDescriptor()}.
|
||||
*
|
||||
* <p>Use the {@link ReactiveAdapterRegistry} to obtain an adapter for a
|
||||
* supported adaptee type or to register additional adapters.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface ReactiveAdapter {
|
||||
|
||||
/**
|
||||
* Return a descriptor with further information about the adaptee.
|
||||
*/
|
||||
Descriptor getDescriptor();
|
||||
|
||||
/**
|
||||
* Adapt the given Object to a {@link Mono}
|
||||
* @param source the source object to adapt
|
||||
* @return the resulting {@link Mono} possibly empty
|
||||
*/
|
||||
<T> Mono<T> toMono(Object source);
|
||||
|
||||
/**
|
||||
* Adapt the given Object to a {@link Flux}.
|
||||
* @param source the source object to adapt
|
||||
* @return the resulting {@link Flux} possibly empty
|
||||
*/
|
||||
<T> Flux<T> toFlux(Object source);
|
||||
|
||||
/**
|
||||
* Adapt the given Object to a Publisher.
|
||||
* @param source the source object to adapt
|
||||
* @return the resulting {@link Mono} or {@link Flux} possibly empty
|
||||
*/
|
||||
<T> Publisher<T> toPublisher(Object source);
|
||||
|
||||
/**
|
||||
* Adapt the given Publisher to the target adaptee.
|
||||
* @param publisher the publisher to adapt
|
||||
* @return the resulting adaptee
|
||||
*/
|
||||
Object fromPublisher(Publisher<?> publisher);
|
||||
|
||||
|
||||
/**
|
||||
* A descriptor with information about the adaptee stream semantics.
|
||||
*/
|
||||
class Descriptor {
|
||||
|
||||
private final boolean isMultiValue;
|
||||
|
||||
private final boolean supportsEmpty;
|
||||
|
||||
private final boolean isNoValue;
|
||||
|
||||
|
||||
public Descriptor(boolean isMultiValue, boolean canBeEmpty, boolean isNoValue) {
|
||||
this.isMultiValue = isMultiValue;
|
||||
this.supportsEmpty = canBeEmpty;
|
||||
this.isNoValue = isNoValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return {@code true} if the adaptee implies 0..N values can be produced
|
||||
* and is therefore a good fit to adapt to {@link Flux}. A {@code false}
|
||||
* return value implies the adaptee will produce 1 value at most and is
|
||||
* therefore a good fit for {@link Mono}.
|
||||
*/
|
||||
public boolean isMultiValue() {
|
||||
return this.isMultiValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the adaptee can complete without values.
|
||||
*/
|
||||
public boolean supportsEmpty() {
|
||||
return this.supportsEmpty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the adaptee implies no values will be produced,
|
||||
* i.e. providing only completion or error signal.
|
||||
*/
|
||||
public boolean isNoValue() {
|
||||
return this.isNoValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* 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.core;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.adapter.RxJava1Adapter;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
|
||||
import org.springframework.core.ReactiveAdapter.Descriptor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A registry of adapters to adapt to {@link Flux} and {@link Mono}.
|
||||
*
|
||||
* <p>By default there are adapters for {@link CompletableFuture}, RxJava 1, and
|
||||
* also for a any Reactive Streams {@link Publisher}. Additional adapters can be
|
||||
* registered via {@link #registerFluxAdapter) and {@link #registerMonoAdapter}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ReactiveAdapterRegistry {
|
||||
|
||||
private static final boolean rxJava1Present =
|
||||
ClassUtils.isPresent("rx.Observable", ReactiveAdapterRegistry.class.getClassLoader());
|
||||
|
||||
|
||||
private final Map<Class<?>, ReactiveAdapter> adapterMap = new LinkedHashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a registry and auto-register default adapters.
|
||||
*/
|
||||
public ReactiveAdapterRegistry() {
|
||||
|
||||
// Flux and Mono ahead of Publisher...
|
||||
registerMonoAdapter(Mono.class,
|
||||
source -> (Mono<?>) source, source -> source, new Descriptor(false, true, false));
|
||||
|
||||
registerFluxAdapter(
|
||||
Flux.class, source -> (Flux<?>) source, source -> source);
|
||||
|
||||
registerFluxAdapter(
|
||||
Publisher.class, source -> Flux.from((Publisher<?>) source), source -> source);
|
||||
|
||||
registerMonoAdapter(CompletableFuture.class,
|
||||
source -> Mono.fromFuture((CompletableFuture<?>) source),
|
||||
source -> Mono.from((Publisher<?>) source).toFuture(),
|
||||
new Descriptor(false, true, false)
|
||||
);
|
||||
|
||||
if (rxJava1Present) {
|
||||
new RxJava1AdapterRegistrar().register(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register an adapter for adapting to and from a {@link Mono}. The provided
|
||||
* functions can assume that input will never be {@code null} and also that
|
||||
* any {@link Optional} wrapper is unwrapped.
|
||||
*/
|
||||
public void registerMonoAdapter(Class<?> adapteeType,
|
||||
Function<Object, Mono<?>> toAdapter, Function<Mono<?>, Object> fromAdapter,
|
||||
Descriptor descriptor) {
|
||||
|
||||
this.adapterMap.put(adapteeType, new MonoReactiveAdapter(toAdapter, fromAdapter, descriptor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an adapter for adapting to and from a {@link Flux}. The provided
|
||||
* functions can assume that input will never be {@code null} and also that
|
||||
* any {@link Optional} wrapper is unwrapped.
|
||||
*/
|
||||
public void registerFluxAdapter(Class<?> adapteeType,
|
||||
Function<Object, Flux<?>> toAdapter, Function<Flux<?>, Object> fromAdapter) {
|
||||
|
||||
this.adapterMap.put(adapteeType, new FluxReactiveAdapter(toAdapter, fromAdapter));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the adapter for the given adaptee type to adapt from.
|
||||
*/
|
||||
public ReactiveAdapter getAdapterFrom(Class<?> adapteeType) {
|
||||
return getAdapterFrom(adapteeType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the adapter for the given adaptee type to adapt from.
|
||||
* If the instance is not {@code null} its actual type is used to check.
|
||||
*/
|
||||
public ReactiveAdapter getAdapterFrom(Class<?> adapteeType, Object adaptee) {
|
||||
Class<?> actualType = getActualType(adapteeType, adaptee);
|
||||
return getAdapterInternal(supportedType -> supportedType.isAssignableFrom(actualType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the adapter for the given adaptee type to adapt to.
|
||||
*/
|
||||
public ReactiveAdapter getAdapterTo(Class<?> adapteeType) {
|
||||
return getAdapterTo(adapteeType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the adapter for the given adaptee type to adapt to.
|
||||
* If the instance is not {@code null} its actual type is used to check.
|
||||
*/
|
||||
public ReactiveAdapter getAdapterTo(Class<?> adapteeType, Object adaptee) {
|
||||
Class<?> actualType = getActualType(adapteeType, adaptee);
|
||||
return getAdapterInternal(supportedType -> supportedType.equals(actualType));
|
||||
}
|
||||
|
||||
|
||||
private static Class<?> getActualType(Class<?> adapteeType, Object adaptee) {
|
||||
adaptee = unwrapOptional(adaptee);
|
||||
return (adaptee != null ? adaptee.getClass() : adapteeType);
|
||||
}
|
||||
|
||||
private static Object unwrapOptional(Object value) {
|
||||
if (value != null && value instanceof Optional) {
|
||||
value = ((Optional<?>) value).orElse(null);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private ReactiveAdapter getAdapterInternal(Predicate<Class<?>> adapteeTypePredicate) {
|
||||
return this.adapterMap.keySet().stream()
|
||||
.filter(adapteeTypePredicate)
|
||||
.map(this.adapterMap::get)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static class MonoReactiveAdapter implements ReactiveAdapter {
|
||||
|
||||
private final Function<Object, Mono<?>> toAdapter;
|
||||
|
||||
private final Function<Mono<?>, Object> fromAdapter;
|
||||
|
||||
private final Descriptor descriptor;
|
||||
|
||||
|
||||
MonoReactiveAdapter(Function<Object, Mono<?>> to, Function<Mono<?>, Object> from, Descriptor descriptor) {
|
||||
this.toAdapter = to;
|
||||
this.fromAdapter = from;
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Descriptor getDescriptor() {
|
||||
return this.descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> toMono(Object source) {
|
||||
source = unwrapOptional(source);
|
||||
if (source == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
return (Mono<T>) this.toAdapter.apply(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> toFlux(Object source) {
|
||||
source = unwrapOptional(source);
|
||||
if (source == null) {
|
||||
return Flux.empty();
|
||||
}
|
||||
return (Flux<T>) this.toMono(source).flux();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Publisher<T> toPublisher(Object source) {
|
||||
return toMono(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fromPublisher(Publisher<?> source) {
|
||||
return (source != null ? this.fromAdapter.apply((Mono<?>) source) : null);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static class FluxReactiveAdapter implements ReactiveAdapter {
|
||||
|
||||
private final Function<Object, Flux<?>> toAdapter;
|
||||
|
||||
private final Function<Flux<?>, Object> fromAdapter;
|
||||
|
||||
private final Descriptor descriptor = new Descriptor(true, true, false);
|
||||
|
||||
|
||||
FluxReactiveAdapter(Function<Object, Flux<?>> to, Function<Flux<?>, Object> from) {
|
||||
this.toAdapter = to;
|
||||
this.fromAdapter = from;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Descriptor getDescriptor() {
|
||||
return this.descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> toMono(Object source) {
|
||||
source = unwrapOptional(source);
|
||||
if (source == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
return (Mono<T>) this.toAdapter.apply(source).next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> toFlux(Object source) {
|
||||
source = unwrapOptional(source);
|
||||
if (source == null) {
|
||||
return Flux.empty();
|
||||
}
|
||||
return (Flux<T>) this.toAdapter.apply(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Publisher<T> toPublisher(Object source) {
|
||||
return toFlux(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fromPublisher(Publisher<?> source) {
|
||||
return (source != null ? this.fromAdapter.apply((Flux<?>) source) : null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RxJava1AdapterRegistrar {
|
||||
|
||||
public void register(ReactiveAdapterRegistry registry) {
|
||||
|
||||
registry.registerFluxAdapter(Observable.class,
|
||||
source -> RxJava1Adapter.observableToFlux((Observable<?>) source),
|
||||
RxJava1Adapter::publisherToObservable
|
||||
);
|
||||
|
||||
registry.registerMonoAdapter(Single.class,
|
||||
source -> RxJava1Adapter.singleToMono((Single<?>) source),
|
||||
RxJava1Adapter::publisherToSingle,
|
||||
new Descriptor(false, false, false)
|
||||
);
|
||||
|
||||
registry.registerMonoAdapter(Completable.class,
|
||||
source -> RxJava1Adapter.completableToMono((Completable) source),
|
||||
RxJava1Adapter::publisherToCompletable,
|
||||
new Descriptor(false, true, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2015 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.core.convert.support;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.GenericConverter;
|
||||
|
||||
/**
|
||||
* Converter to adapt {@link CompletableFuture} to Reactive Streams and
|
||||
* Reactor {@link Mono}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
*/
|
||||
public class MonoToCompletableFutureConverter implements GenericConverter {
|
||||
|
||||
|
||||
@Override
|
||||
public Set<GenericConverter.ConvertiblePair> getConvertibleTypes() {
|
||||
Set<GenericConverter.ConvertiblePair> pairs = new LinkedHashSet<>(2);
|
||||
pairs.add(new GenericConverter.ConvertiblePair(Mono.class, CompletableFuture.class));
|
||||
pairs.add(new GenericConverter.ConvertiblePair(CompletableFuture.class, Mono.class));
|
||||
return pairs;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
else if (CompletableFuture.class.isAssignableFrom(sourceType.getType())) {
|
||||
return Mono.fromFuture((CompletableFuture<?>) source);
|
||||
}
|
||||
else if (CompletableFuture.class.isAssignableFrom(targetType.getType())) {
|
||||
return Mono.from((Publisher<?>) source).toFuture();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2015 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.core.convert.support;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.adapter.RxJava1Adapter;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.GenericConverter;
|
||||
|
||||
/**
|
||||
* Converter to adapt RxJava1 {@link Observable}, {@link Single}, and
|
||||
* {@link Completable} to Reactive Streams and Reactor types.
|
||||
*
|
||||
* @author Stephane Maldini
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class ReactorToRxJava1Converter implements GenericConverter {
|
||||
|
||||
@Override
|
||||
public Set<GenericConverter.ConvertiblePair> getConvertibleTypes() {
|
||||
Set<GenericConverter.ConvertiblePair> pairs = new LinkedHashSet<>(6);
|
||||
pairs.add(new GenericConverter.ConvertiblePair(Flux.class, Observable.class));
|
||||
pairs.add(new GenericConverter.ConvertiblePair(Observable.class, Flux.class));
|
||||
pairs.add(new GenericConverter.ConvertiblePair(Mono.class, Single.class));
|
||||
pairs.add(new GenericConverter.ConvertiblePair(Single.class, Mono.class));
|
||||
pairs.add(new GenericConverter.ConvertiblePair(Mono.class, Completable.class));
|
||||
pairs.add(new GenericConverter.ConvertiblePair(Completable.class, Mono.class));
|
||||
return pairs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
if (Observable.class.isAssignableFrom(sourceType.getType())) {
|
||||
return RxJava1Adapter.observableToFlux((Observable<?>) source);
|
||||
}
|
||||
else if (Observable.class.isAssignableFrom(targetType.getType())) {
|
||||
return RxJava1Adapter.publisherToObservable((Publisher<?>) source);
|
||||
}
|
||||
else if (Single.class.isAssignableFrom(sourceType.getType())) {
|
||||
return RxJava1Adapter.singleToMono((Single<?>) source);
|
||||
}
|
||||
else if (Single.class.isAssignableFrom(targetType.getType())) {
|
||||
return RxJava1Adapter.publisherToSingle((Publisher<?>) source);
|
||||
}
|
||||
else if (Completable.class.isAssignableFrom(sourceType.getType())) {
|
||||
return RxJava1Adapter.completableToMono((Completable) source);
|
||||
}
|
||||
else if (Completable.class.isAssignableFrom(targetType.getType())) {
|
||||
return RxJava1Adapter.publisherToCompletable((Publisher<?>) source);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,56 +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.core.convert.support;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ReactorToRxJava1Converter}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class MonoToCompletableFutureConverterTests {
|
||||
|
||||
private GenericConversionService conversionService;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.conversionService = new GenericConversionService();
|
||||
this.conversionService.addConverter(new MonoToCompletableFutureConverter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canConvert() throws Exception {
|
||||
assertTrue(this.conversionService.canConvert(Mono.class, CompletableFuture.class));
|
||||
assertTrue(this.conversionService.canConvert(CompletableFuture.class, Mono.class));
|
||||
|
||||
assertFalse(this.conversionService.canConvert(Flux.class, CompletableFuture.class));
|
||||
assertFalse(this.conversionService.canConvert(CompletableFuture.class, Flux.class));
|
||||
|
||||
assertFalse(this.conversionService.canConvert(Publisher.class, CompletableFuture.class));
|
||||
assertFalse(this.conversionService.canConvert(CompletableFuture.class, Publisher.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.core.convert.support;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ReactiveAdapterRegistry}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ReactiveAdapterRegistryTests {
|
||||
|
||||
private ReactiveAdapterRegistry adapterRegistry;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.adapterRegistry = new ReactiveAdapterRegistry();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDefaultAdapters() throws Exception {
|
||||
testMonoAdapter(Mono.class);
|
||||
testFluxAdapter(Flux.class);
|
||||
testFluxAdapter(Publisher.class);
|
||||
testMonoAdapter(CompletableFuture.class);
|
||||
testFluxAdapter(Observable.class);
|
||||
testMonoAdapter(Single.class);
|
||||
testMonoAdapter(Completable.class);
|
||||
}
|
||||
|
||||
private void testFluxAdapter(Class<?> adapteeType) {
|
||||
ReactiveAdapter adapter = this.adapterRegistry.getAdapterFrom(adapteeType);
|
||||
assertNotNull(adapter);
|
||||
assertTrue(adapter.getDescriptor().isMultiValue());
|
||||
|
||||
adapter = this.adapterRegistry.getAdapterTo(adapteeType);
|
||||
assertNotNull(adapter);
|
||||
assertTrue(adapter.getDescriptor().isMultiValue());
|
||||
}
|
||||
|
||||
private void testMonoAdapter(Class<?> adapteeType) {
|
||||
ReactiveAdapter adapter = this.adapterRegistry.getAdapterFrom(adapteeType);
|
||||
assertNotNull(adapter);
|
||||
assertFalse(adapter.getDescriptor().isMultiValue());
|
||||
|
||||
adapter = this.adapterRegistry.getAdapterTo(adapteeType);
|
||||
assertNotNull(adapter);
|
||||
assertFalse(adapter.getDescriptor().isMultiValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,69 +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.core.convert.support;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ReactorToRxJava1Converter}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ReactorToRxJava1ConverterTests {
|
||||
|
||||
private GenericConversionService conversionService;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.conversionService = new GenericConversionService();
|
||||
this.conversionService.addConverter(new ReactorToRxJava1Converter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canConvert() throws Exception {
|
||||
assertTrue(this.conversionService.canConvert(Flux.class, Observable.class));
|
||||
assertTrue(this.conversionService.canConvert(Observable.class, Flux.class));
|
||||
|
||||
assertTrue(this.conversionService.canConvert(Mono.class, Single.class));
|
||||
assertTrue(this.conversionService.canConvert(Single.class, Mono.class));
|
||||
|
||||
assertTrue(this.conversionService.canConvert(Mono.class, Completable.class));
|
||||
assertTrue(this.conversionService.canConvert(Completable.class, Mono.class));
|
||||
|
||||
assertFalse(this.conversionService.canConvert(Flux.class, Single.class));
|
||||
assertFalse(this.conversionService.canConvert(Single.class, Flux.class));
|
||||
|
||||
assertFalse(this.conversionService.canConvert(Flux.class, Completable.class));
|
||||
assertFalse(this.conversionService.canConvert(Completable.class, Flux.class));
|
||||
|
||||
assertFalse(this.conversionService.canConvert(Mono.class, Observable.class));
|
||||
assertFalse(this.conversionService.canConvert(Observable.class, Mono.class));
|
||||
|
||||
assertFalse(this.conversionService.canConvert(Publisher.class, Observable.class));
|
||||
assertFalse(this.conversionService.canConvert(Observable.class, Publisher.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -34,8 +34,6 @@ import org.springframework.core.codec.ResourceDecoder;
|
|||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.codec.StringEncoder;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
|
||||
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
|
|
@ -274,17 +272,8 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
|||
|
||||
/**
|
||||
* Override to add custom {@link Converter}s and {@link Formatter}s.
|
||||
* <p>By default this method method registers:
|
||||
* <ul>
|
||||
* <li>{@link MonoToCompletableFutureConverter}
|
||||
* <li>{@link ReactorToRxJava1Converter}
|
||||
* </ul>
|
||||
*/
|
||||
protected void addFormatters(FormatterRegistry registry) {
|
||||
registry.addConverter(new MonoToCompletableFutureConverter());
|
||||
if (rxJava1Present) {
|
||||
registry.addConverter(new ReactorToRxJava1Converter());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -334,19 +323,17 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
|||
|
||||
@Bean
|
||||
public SimpleResultHandler simpleResultHandler() {
|
||||
return new SimpleResultHandler(mvcConversionService());
|
||||
return new SimpleResultHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResponseEntityResultHandler responseEntityResultHandler() {
|
||||
return new ResponseEntityResultHandler(getMessageWriters(), mvcConversionService(),
|
||||
mvcContentTypeResolver());
|
||||
return new ResponseEntityResultHandler(getMessageWriters(), mvcContentTypeResolver());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResponseBodyResultHandler responseBodyResultHandler() {
|
||||
return new ResponseBodyResultHandler(getMessageWriters(), mvcConversionService(),
|
||||
mvcContentTypeResolver());
|
||||
return new ResponseBodyResultHandler(getMessageWriters(), mvcContentTypeResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -405,7 +392,7 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
|||
ViewResolverRegistry registry = new ViewResolverRegistry(getApplicationContext());
|
||||
configureViewResolvers(registry);
|
||||
List<ViewResolver> resolvers = registry.getViewResolvers();
|
||||
ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, mvcConversionService());
|
||||
ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, mvcContentTypeResolver());
|
||||
handler.setDefaultViews(registry.getDefaultViews());
|
||||
handler.setOrder(registry.getOrder());
|
||||
return handler;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import java.util.Optional;
|
|||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
|
|
@ -43,28 +43,32 @@ public abstract class ContentNegotiatingResultHandlerSupport implements Ordered
|
|||
private static final MediaType MEDIA_TYPE_APPLICATION_ALL = new MediaType("application");
|
||||
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
private final RequestedContentTypeResolver contentTypeResolver;
|
||||
|
||||
private final ReactiveAdapterRegistry adapterRegistry;
|
||||
|
||||
private int order = LOWEST_PRECEDENCE;
|
||||
|
||||
|
||||
protected ContentNegotiatingResultHandlerSupport(ConversionService conversionService,
|
||||
RequestedContentTypeResolver contentTypeResolver) {
|
||||
protected ContentNegotiatingResultHandlerSupport(RequestedContentTypeResolver contentTypeResolver) {
|
||||
this(contentTypeResolver, new ReactiveAdapterRegistry());
|
||||
}
|
||||
|
||||
protected ContentNegotiatingResultHandlerSupport(RequestedContentTypeResolver contentTypeResolver,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
Assert.notNull(conversionService, "'conversionService' is required.");
|
||||
Assert.notNull(contentTypeResolver, "'contentTypeResolver' is required.");
|
||||
this.conversionService = conversionService;
|
||||
Assert.notNull(adapterRegistry, "'adapterRegistry' is required.");
|
||||
this.contentTypeResolver = contentTypeResolver;
|
||||
this.adapterRegistry = adapterRegistry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the configured {@link ConversionService}.
|
||||
* Return the configured {@link ReactiveAdapterRegistry}.
|
||||
*/
|
||||
public ConversionService getConversionService() {
|
||||
return this.conversionService;
|
||||
public ReactiveAdapterRegistry getReactiveAdapterRegistry() {
|
||||
return this.adapterRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,14 +18,12 @@ package org.springframework.web.reactive.result;
|
|||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.HandlerResultHandler;
|
||||
|
|
@ -45,27 +43,26 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
*/
|
||||
public class SimpleResultHandler implements Ordered, HandlerResultHandler {
|
||||
|
||||
protected static final TypeDescriptor MONO_TYPE = TypeDescriptor.valueOf(Mono.class);
|
||||
|
||||
protected static final TypeDescriptor FLUX_TYPE = TypeDescriptor.valueOf(Flux.class);
|
||||
|
||||
|
||||
private ConversionService conversionService;
|
||||
private ReactiveAdapterRegistry adapterRegistry;
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
|
||||
public SimpleResultHandler(ConversionService conversionService) {
|
||||
Assert.notNull(conversionService, "'conversionService' is required.");
|
||||
this.conversionService = conversionService;
|
||||
public SimpleResultHandler() {
|
||||
this.adapterRegistry = new ReactiveAdapterRegistry();
|
||||
}
|
||||
|
||||
public SimpleResultHandler(ReactiveAdapterRegistry adapterRegistry) {
|
||||
Assert.notNull(adapterRegistry, "'adapterRegistry' is required.");
|
||||
this.adapterRegistry = adapterRegistry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the configured {@link ConversionService}.
|
||||
* Return the configured {@link ReactiveAdapterRegistry}.
|
||||
*/
|
||||
public ConversionService getConversionService() {
|
||||
return this.conversionService;
|
||||
public ReactiveAdapterRegistry getAdapterRegistry() {
|
||||
return this.adapterRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -88,37 +85,28 @@ public class SimpleResultHandler implements Ordered, HandlerResultHandler {
|
|||
@Override
|
||||
public boolean supports(HandlerResult result) {
|
||||
ResolvableType type = result.getReturnType();
|
||||
if (Void.TYPE.equals(type.getRawClass())) {
|
||||
Class<?> rawClass = type.getRawClass();
|
||||
if (Void.TYPE.equals(rawClass)) {
|
||||
return true;
|
||||
}
|
||||
TypeDescriptor source = new TypeDescriptor(result.getReturnTypeSource());
|
||||
if (Publisher.class.isAssignableFrom(type.getRawClass()) ||
|
||||
canConvert(source, MONO_TYPE) || canConvert(source, FLUX_TYPE)) {
|
||||
Class<?> clazz = result.getReturnType().getGeneric(0).getRawClass();
|
||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapterFrom(rawClass, result.getReturnValue());
|
||||
if (adapter != null) {
|
||||
Class<?> clazz = type.getGeneric(0).getRawClass();
|
||||
return Void.class.equals(clazz);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean canConvert(TypeDescriptor source, TypeDescriptor target) {
|
||||
return getConversionService().canConvert(source, target);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
|
||||
Optional<Object> optional = result.getReturnValue();
|
||||
if (!optional.isPresent()) {
|
||||
Optional<Object> optionalValue = result.getReturnValue();
|
||||
if (!optionalValue.isPresent()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
Object value = optional.get();
|
||||
if (Publisher.class.isAssignableFrom(result.getReturnType().getRawClass())) {
|
||||
return Mono.from((Publisher<?>) value).then();
|
||||
}
|
||||
TypeDescriptor source = new TypeDescriptor(result.getReturnTypeSource());
|
||||
return canConvert(source, MONO_TYPE) ?
|
||||
((Mono<Void>) getConversionService().convert(value, source, MONO_TYPE)) :
|
||||
((Flux<Void>) getConversionService().convert(value, source, FLUX_TYPE)).single();
|
||||
Class<?> returnType = result.getReturnType().getRawClass();
|
||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapterFrom(returnType, optionalValue);
|
||||
return adapter.toMono(optionalValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ import reactor.core.publisher.Mono;
|
|||
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
|
|
@ -57,34 +57,39 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
|||
*/
|
||||
public abstract class AbstractMessageReaderArgumentResolver {
|
||||
|
||||
private static final TypeDescriptor MONO_TYPE = TypeDescriptor.valueOf(Mono.class);
|
||||
|
||||
private static final TypeDescriptor FLUX_TYPE = TypeDescriptor.valueOf(Flux.class);
|
||||
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
private final Validator validator;
|
||||
|
||||
private final ReactiveAdapterRegistry adapterRegistry;
|
||||
|
||||
private final List<MediaType> supportedMediaTypes;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a ConversionService.
|
||||
* @param messageReaders readers to convert from the request body
|
||||
* @param service for converting to other reactive types from Flux and Mono
|
||||
* Constructor with {@link HttpMessageReader}'s and a {@link Validator}.
|
||||
* @param readers readers to convert from the request body
|
||||
* @param validator validator to validate decoded objects with
|
||||
*/
|
||||
protected AbstractMessageReaderArgumentResolver(List<HttpMessageReader<?>> messageReaders,
|
||||
ConversionService service, Validator validator) {
|
||||
protected AbstractMessageReaderArgumentResolver(List<HttpMessageReader<?>> readers, Validator validator) {
|
||||
|
||||
Assert.notEmpty(messageReaders, "At least one message reader is required.");
|
||||
Assert.notNull(service, "'conversionService' is required.");
|
||||
this(readers, validator, new ReactiveAdapterRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that also accepts a {@link ReactiveAdapterRegistry}.
|
||||
* @param messageReaders readers to convert from the request body
|
||||
* @param validator validator to validate decoded objects with
|
||||
* @param adapterRegistry for adapting to other reactive types from Flux and Mono
|
||||
*/
|
||||
protected AbstractMessageReaderArgumentResolver(List<HttpMessageReader<?>> messageReaders,
|
||||
Validator validator, ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
Assert.notEmpty(messageReaders, "At least one HttpMessageReader is required.");
|
||||
Assert.notNull(adapterRegistry, "'adapterRegistry' is required");
|
||||
this.messageReaders = messageReaders;
|
||||
this.conversionService = service;
|
||||
this.validator = validator;
|
||||
this.adapterRegistry = adapterRegistry;
|
||||
this.supportedMediaTypes = messageReaders.stream()
|
||||
.flatMap(converter -> converter.getReadableMediaTypes().stream())
|
||||
.collect(Collectors.toList());
|
||||
|
|
@ -99,22 +104,21 @@ public abstract class AbstractMessageReaderArgumentResolver {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the configured {@link ConversionService}.
|
||||
* Return the configured {@link ReactiveAdapterRegistry}.
|
||||
*/
|
||||
public ConversionService getConversionService() {
|
||||
return this.conversionService;
|
||||
public ReactiveAdapterRegistry getReactiveAdapterRegistry() {
|
||||
return this.adapterRegistry;
|
||||
}
|
||||
|
||||
|
||||
protected Mono<Object> readBody(MethodParameter bodyParameter, boolean isBodyRequired,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(bodyParameter);
|
||||
boolean convertFromMono = getConversionService().canConvert(MONO_TYPE, typeDescriptor);
|
||||
boolean convertFromFlux = getConversionService().canConvert(FLUX_TYPE, typeDescriptor);
|
||||
Class<?> bodyType = ResolvableType.forMethodParameter(bodyParameter).resolve();
|
||||
ReactiveAdapter adapter = getReactiveAdapterRegistry().getAdapterTo(bodyType);
|
||||
|
||||
ResolvableType elementType = ResolvableType.forMethodParameter(bodyParameter);
|
||||
if (convertFromMono || convertFromFlux) {
|
||||
if (adapter != null) {
|
||||
elementType = elementType.getGeneric(0);
|
||||
}
|
||||
|
||||
|
|
@ -126,28 +130,28 @@ public abstract class AbstractMessageReaderArgumentResolver {
|
|||
|
||||
for (HttpMessageReader<?> reader : getMessageReaders()) {
|
||||
if (reader.canRead(elementType, mediaType)) {
|
||||
if (convertFromFlux) {
|
||||
if (adapter != null && adapter.getDescriptor().isMultiValue()) {
|
||||
Flux<?> flux = reader.read(elementType, request)
|
||||
.onErrorResumeWith(ex -> Flux.error(getReadError(ex, bodyParameter)));
|
||||
if (checkRequired(bodyParameter, isBodyRequired)) {
|
||||
if (checkRequired(adapter, isBodyRequired)) {
|
||||
flux = flux.switchIfEmpty(Flux.error(getRequiredBodyError(bodyParameter)));
|
||||
}
|
||||
if (this.validator != null) {
|
||||
flux = flux.map(applyValidationIfApplicable(bodyParameter));
|
||||
}
|
||||
return Mono.just(getConversionService().convert(flux, FLUX_TYPE, typeDescriptor));
|
||||
return Mono.just(adapter.fromPublisher(flux));
|
||||
}
|
||||
else {
|
||||
Mono<?> mono = reader.readMono(elementType, request)
|
||||
.otherwise(ex -> Mono.error(getReadError(ex, bodyParameter)));
|
||||
if (checkRequired(bodyParameter, isBodyRequired)) {
|
||||
if (checkRequired(adapter, isBodyRequired)) {
|
||||
mono = mono.otherwiseIfEmpty(Mono.error(getRequiredBodyError(bodyParameter)));
|
||||
}
|
||||
if (this.validator != null) {
|
||||
mono = mono.map(applyValidationIfApplicable(bodyParameter));
|
||||
}
|
||||
if (convertFromMono) {
|
||||
return Mono.just(getConversionService().convert(mono, MONO_TYPE, typeDescriptor));
|
||||
if (adapter != null) {
|
||||
return Mono.just(adapter.fromPublisher(mono));
|
||||
}
|
||||
else {
|
||||
return Mono.from(mono);
|
||||
|
|
@ -159,11 +163,8 @@ public abstract class AbstractMessageReaderArgumentResolver {
|
|||
return Mono.error(new UnsupportedMediaTypeStatusException(mediaType, this.supportedMediaTypes));
|
||||
}
|
||||
|
||||
protected boolean checkRequired(MethodParameter bodyParameter, boolean isBodyRequired) {
|
||||
if ("rx.Single".equals(bodyParameter.getNestedParameterType().getName())) {
|
||||
return true;
|
||||
}
|
||||
return isBodyRequired;
|
||||
protected boolean checkRequired(ReactiveAdapter adapter, boolean isBodyRequired) {
|
||||
return adapter != null && !adapter.getDescriptor().supportsEmpty() || isBodyRequired;
|
||||
}
|
||||
|
||||
protected ServerWebInputException getReadError(Throwable ex, MethodParameter parameter) {
|
||||
|
|
|
|||
|
|
@ -19,13 +19,12 @@ import java.util.List;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
|
|
@ -44,31 +43,42 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
*/
|
||||
public abstract class AbstractMessageWriterResultHandler extends ContentNegotiatingResultHandlerSupport {
|
||||
|
||||
protected static final TypeDescriptor MONO_TYPE = TypeDescriptor.valueOf(Mono.class);
|
||||
|
||||
protected static final TypeDescriptor FLUX_TYPE = TypeDescriptor.valueOf(Flux.class);
|
||||
|
||||
|
||||
private final List<HttpMessageWriter<?>> messageWriters;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor with message converters, a {@code ConversionService}, and a
|
||||
* Constructor with {@link HttpMessageWriter}s and a
|
||||
* {@code RequestedContentTypeResolver}.
|
||||
*
|
||||
* @param messageWriters for serializing Objects to the response body stream
|
||||
* @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 AbstractMessageWriterResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) {
|
||||
RequestedContentTypeResolver contentTypeResolver) {
|
||||
|
||||
super(conversionService, contentTypeResolver);
|
||||
super(contentTypeResolver);
|
||||
Assert.notEmpty(messageWriters, "At least one message writer is required.");
|
||||
this.messageWriters = messageWriters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with an additional {@link ReactiveAdapterRegistry}.
|
||||
*
|
||||
* @param messageWriters for serializing Objects to the response body stream
|
||||
* @param contentTypeResolver for resolving the requested content type
|
||||
* @param adapterRegistry for adapting other reactive types (e.g. rx.Observable,
|
||||
* rx.Single, etc.) to Flux or Mono
|
||||
*/
|
||||
protected AbstractMessageWriterResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
RequestedContentTypeResolver contentTypeResolver,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(contentTypeResolver, adapterRegistry);
|
||||
Assert.notEmpty(messageWriters, "At least one message writer is required.");
|
||||
this.messageWriters = messageWriters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the configured message converters.
|
||||
*/
|
||||
|
|
@ -78,31 +88,20 @@ public abstract class AbstractMessageWriterResultHandler extends ContentNegotiat
|
|||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Mono<Void> writeBody(ServerWebExchange exchange, Object body,
|
||||
ResolvableType bodyType, MethodParameter bodyTypeParameter) {
|
||||
protected Mono<Void> writeBody(Object body, MethodParameter bodyType, ServerWebExchange exchange) {
|
||||
|
||||
Publisher<?> publisher = null;
|
||||
Class<?> bodyClass = bodyType.getParameterType();
|
||||
ReactiveAdapter adapter = getReactiveAdapterRegistry().getAdapterFrom(bodyClass, body);
|
||||
|
||||
Publisher<?> publisher;
|
||||
ResolvableType elementType;
|
||||
|
||||
if (Publisher.class.isAssignableFrom(bodyType.getRawClass())) {
|
||||
publisher = (Publisher<?>) body;
|
||||
if (adapter != null) {
|
||||
publisher = adapter.toPublisher(body);
|
||||
elementType = ResolvableType.forMethodParameter(bodyType).getGeneric(0);
|
||||
}
|
||||
else {
|
||||
TypeDescriptor descriptor = new TypeDescriptor(bodyTypeParameter);
|
||||
if (getConversionService().canConvert(descriptor, MONO_TYPE)) {
|
||||
publisher = (Publisher<?>) getConversionService().convert(body, descriptor, MONO_TYPE);
|
||||
}
|
||||
else if (getConversionService().canConvert(descriptor, FLUX_TYPE)) {
|
||||
publisher = (Publisher<?>) getConversionService().convert(body, descriptor, FLUX_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
if (publisher != null) {
|
||||
elementType = bodyType.getGeneric(0);
|
||||
}
|
||||
else {
|
||||
elementType = bodyType;
|
||||
publisher = Mono.justOrEmpty(body);
|
||||
elementType = ResolvableType.forMethodParameter(bodyType);
|
||||
}
|
||||
|
||||
if (void.class == elementType.getRawClass() || Void.class == elementType.getRawClass()) {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import java.util.List;
|
|||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
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;
|
||||
|
|
@ -45,26 +45,24 @@ public class HttpEntityArgumentResolver extends AbstractMessageReaderArgumentRes
|
|||
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a ConversionService.
|
||||
* @param messageReaders readers for de-serializing the request body with
|
||||
* @param service for converting to other reactive types from Flux and Mono
|
||||
* Constructor with {@link HttpMessageReader}'s and a {@link Validator}.
|
||||
* @param readers readers for de-serializing the request body with
|
||||
* @param validator validator to validate decoded objects with
|
||||
*/
|
||||
public HttpEntityArgumentResolver(List<HttpMessageReader<?>> messageReaders,
|
||||
ConversionService service) {
|
||||
|
||||
this(messageReaders, service, null);
|
||||
public HttpEntityArgumentResolver(List<HttpMessageReader<?>> readers, Validator validator) {
|
||||
super(readers, validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a ConversionService.
|
||||
* @param messageReaders readers for de-serializing the request body with
|
||||
* @param service for converting to other reactive types from Flux and Mono
|
||||
* Constructor that also accepts a {@link ReactiveAdapterRegistry}.
|
||||
* @param readers readers for de-serializing the request body with
|
||||
* @param validator validator to validate decoded objects with
|
||||
* @param adapterRegistry for adapting to other reactive types from Flux and Mono
|
||||
*/
|
||||
public HttpEntityArgumentResolver(List<HttpMessageReader<?>> messageReaders,
|
||||
ConversionService service, Validator validator) {
|
||||
public HttpEntityArgumentResolver(List<HttpMessageReader<?>> readers, Validator validator,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(messageReaders, service, validator);
|
||||
super(readers, validator, adapterRegistry);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -77,20 +75,9 @@ public class HttpEntityArgumentResolver extends AbstractMessageReaderArgumentRes
|
|||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter param, ModelMap model, ServerWebExchange exchange) {
|
||||
|
||||
ResolvableType entityType;
|
||||
MethodParameter bodyParameter;
|
||||
|
||||
if (getConversionService().canConvert(Mono.class, param.getParameterType())) {
|
||||
entityType = ResolvableType.forMethodParameter(param).getGeneric(0);
|
||||
bodyParameter = new MethodParameter(param);
|
||||
ResolvableType entityType = ResolvableType.forMethodParameter(param);
|
||||
MethodParameter bodyParameter = new MethodParameter(param);
|
||||
bodyParameter.increaseNestingLevel();
|
||||
bodyParameter.increaseNestingLevel();
|
||||
}
|
||||
else {
|
||||
entityType = ResolvableType.forMethodParameter(param);
|
||||
bodyParameter = new MethodParameter(param);
|
||||
bodyParameter.increaseNestingLevel();
|
||||
}
|
||||
|
||||
return readBody(bodyParameter, false, exchange)
|
||||
.map(body -> createHttpEntity(body, entityType, exchange))
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import java.util.List;
|
|||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.Validator;
|
||||
|
|
@ -49,26 +49,24 @@ public class RequestBodyArgumentResolver extends AbstractMessageReaderArgumentRe
|
|||
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a ConversionService.
|
||||
* @param messageReaders readers for de-serializing the request body with
|
||||
* @param service for converting to other reactive types from Flux and Mono
|
||||
* Constructor with {@link HttpMessageReader}'s and a {@link Validator}.
|
||||
* @param readers readers for de-serializing the request body with
|
||||
* @param validator validator to validate decoded objects with
|
||||
*/
|
||||
public RequestBodyArgumentResolver(List<HttpMessageReader<?>> messageReaders,
|
||||
ConversionService service) {
|
||||
|
||||
this(messageReaders, service, null);
|
||||
public RequestBodyArgumentResolver(List<HttpMessageReader<?>> readers, Validator validator) {
|
||||
super(readers, validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a ConversionService.
|
||||
* @param messageReaders readers for de-serializing the request body with
|
||||
* @param service for converting to other reactive types from Flux and Mono
|
||||
* Constructor that also accepts a {@link ReactiveAdapterRegistry}.
|
||||
* @param readers readers for de-serializing the request body with
|
||||
* @param validator validator to validate decoded objects with
|
||||
* @param adapterRegistry for adapting to other reactive types from Flux and Mono
|
||||
*/
|
||||
public RequestBodyArgumentResolver(List<HttpMessageReader<?>> messageReaders,
|
||||
ConversionService service, Validator validator) {
|
||||
public RequestBodyArgumentResolver(List<HttpMessageReader<?>> readers, Validator validator,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(messageReaders, service, validator);
|
||||
super(readers, validator, adapterRegistry);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.springframework.beans.factory.BeanFactory;
|
|||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.codec.ByteBufferDecoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
|
|
@ -66,6 +67,8 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
|
||||
private final List<HttpMessageReader<?>> messageReaders = new ArrayList<>(10);
|
||||
|
||||
private ReactiveAdapterRegistry reactiveAdapters = new ReactiveAdapterRegistry();
|
||||
|
||||
private ConversionService conversionService = new DefaultFormattingConversionService();
|
||||
|
||||
private Validator validator;
|
||||
|
|
@ -126,6 +129,14 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
return this.messageReaders;
|
||||
}
|
||||
|
||||
public void setReactiveAdapterRegistry(ReactiveAdapterRegistry registry) {
|
||||
this.reactiveAdapters = registry;
|
||||
}
|
||||
|
||||
public ReactiveAdapterRegistry getReactiveAdapterRegistry() {
|
||||
return this.reactiveAdapters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a ConversionService for type conversion of controller method
|
||||
* arguments as well as for converting from different async types to
|
||||
|
|
@ -187,13 +198,15 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
protected List<HandlerMethodArgumentResolver> initArgumentResolvers() {
|
||||
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
|
||||
|
||||
// Annotation-based argument resolution
|
||||
ConversionService cs = getConversionService();
|
||||
ReactiveAdapterRegistry adapterRegistry = getReactiveAdapterRegistry();
|
||||
|
||||
// Annotation-based argument resolution
|
||||
resolvers.add(new RequestParamMethodArgumentResolver(cs, getBeanFactory(), false));
|
||||
resolvers.add(new RequestParamMapMethodArgumentResolver());
|
||||
resolvers.add(new PathVariableMethodArgumentResolver(cs, getBeanFactory()));
|
||||
resolvers.add(new PathVariableMapMethodArgumentResolver());
|
||||
resolvers.add(new RequestBodyArgumentResolver(getMessageReaders(), cs, getValidator()));
|
||||
resolvers.add(new RequestBodyArgumentResolver(getMessageReaders(), getValidator(), adapterRegistry));
|
||||
resolvers.add(new RequestHeaderMethodArgumentResolver(cs, getBeanFactory()));
|
||||
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
|
||||
resolvers.add(new CookieValueMethodArgumentResolver(cs, getBeanFactory()));
|
||||
|
|
@ -202,6 +215,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
resolvers.add(new RequestAttributeMethodArgumentResolver(cs , getBeanFactory()));
|
||||
|
||||
// Type-based argument resolution
|
||||
resolvers.add(new HttpEntityArgumentResolver(getMessageReaders(), getValidator(), adapterRegistry));
|
||||
resolvers.add(new ModelArgumentResolver());
|
||||
|
||||
// Custom resolvers
|
||||
|
|
|
|||
|
|
@ -21,15 +21,14 @@ import java.util.List;
|
|||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
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;
|
||||
|
||||
|
|
@ -53,42 +52,41 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
public class ResponseBodyResultHandler extends AbstractMessageWriterResultHandler
|
||||
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 messageWriters writers for serializing to the response body stream
|
||||
* @param conversionService for converting to Flux and Mono from other reactive types
|
||||
*/
|
||||
public ResponseBodyResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
ConversionService conversionService) {
|
||||
|
||||
this(messageWriters, conversionService, new HeaderContentTypeResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with message converters, a {@code ConversionService}, and a
|
||||
* Constructor with {@link HttpMessageWriter}s and a
|
||||
* {@code RequestedContentTypeResolver}.
|
||||
*
|
||||
* @param messageWriters writers for serializing to the response body stream
|
||||
* @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 ResponseBodyResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) {
|
||||
RequestedContentTypeResolver contentTypeResolver) {
|
||||
|
||||
super(messageWriters, conversionService, contentTypeResolver);
|
||||
this(messageWriters, contentTypeResolver, new ReactiveAdapterRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with an additional {@link ReactiveAdapterRegistry}.
|
||||
*
|
||||
* @param messageWriters writers for serializing to the response body stream
|
||||
* @param contentTypeResolver for resolving the requested content type
|
||||
* @param adapterRegistry for adapting other reactive types (e.g. rx.Observable,
|
||||
* rx.Single, etc.) to Flux or Mono
|
||||
*/
|
||||
public ResponseBodyResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
RequestedContentTypeResolver contentTypeResolver,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(messageWriters, contentTypeResolver, adapterRegistry);
|
||||
setOrder(100);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(HandlerResult result) {
|
||||
ResolvableType returnType = result.getReturnType();
|
||||
MethodParameter parameter = result.getReturnTypeSource();
|
||||
return hasResponseBodyAnnotation(parameter) && !isHttpEntityType(returnType);
|
||||
return hasResponseBodyAnnotation(parameter) && !isHttpEntityType(result);
|
||||
}
|
||||
|
||||
private boolean hasResponseBodyAnnotation(MethodParameter parameter) {
|
||||
|
|
@ -97,26 +95,27 @@ public class ResponseBodyResultHandler extends AbstractMessageWriterResultHandle
|
|||
parameter.getMethodAnnotation(ResponseBody.class) != null);
|
||||
}
|
||||
|
||||
private boolean isHttpEntityType(ResolvableType returnType) {
|
||||
if (HttpEntity.class.isAssignableFrom(returnType.getRawClass())) {
|
||||
private boolean isHttpEntityType(HandlerResult result) {
|
||||
Class<?> rawClass = result.getReturnType().getRawClass();
|
||||
if (HttpEntity.class.isAssignableFrom(rawClass)) {
|
||||
return true;
|
||||
}
|
||||
else if (getConversionService().canConvert(returnType.getRawClass(), Mono.class)) {
|
||||
ResolvableType genericType = returnType.getGeneric(0);
|
||||
else {
|
||||
if (getReactiveAdapterRegistry().getAdapterFrom(rawClass, result.getReturnValue()) != null) {
|
||||
ResolvableType genericType = result.getReturnType().getGeneric(0);
|
||||
if (HttpEntity.class.isAssignableFrom(genericType.getRawClass())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
|
||||
Object body = result.getReturnValue().orElse(null);
|
||||
ResolvableType bodyType = result.getReturnType();
|
||||
MethodParameter bodyTypeParameter = result.getReturnTypeSource();
|
||||
return writeBody(exchange, body, bodyType, bodyTypeParameter);
|
||||
return writeBody(body, bodyTypeParameter, exchange);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ import java.util.Optional;
|
|||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
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;
|
||||
|
|
@ -31,7 +32,6 @@ import org.springframework.http.converter.reactive.HttpMessageWriter;
|
|||
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;
|
||||
|
||||
|
|
@ -47,52 +47,55 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
public class ResponseEntityResultHandler extends AbstractMessageWriterResultHandler
|
||||
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 messageWriters writers for serializing to the response body stream
|
||||
* @param conversionService for converting to Flux and Mono from other reactive types
|
||||
*/
|
||||
public ResponseEntityResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
ConversionService conversionService) {
|
||||
|
||||
this(messageWriters, conversionService, new HeaderContentTypeResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with message converters, a {@code ConversionService}, and a
|
||||
* Constructor with {@link HttpMessageWriter}s and a
|
||||
* {@code RequestedContentTypeResolver}.
|
||||
*
|
||||
* @param messageWriters writers for serializing to the response body stream
|
||||
* @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<HttpMessageWriter<?>> messageWriters,
|
||||
ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) {
|
||||
RequestedContentTypeResolver contentTypeResolver) {
|
||||
|
||||
super(messageWriters, conversionService, contentTypeResolver);
|
||||
this(messageWriters, contentTypeResolver, new ReactiveAdapterRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with an additional {@link ReactiveAdapterRegistry}.
|
||||
*
|
||||
* @param messageWriters writers for serializing to the response body stream
|
||||
* @param contentTypeResolver for resolving the requested content type
|
||||
* @param adapterRegistry for adapting other reactive types (e.g. rx.Observable,
|
||||
* rx.Single, etc.) to Flux or Mono
|
||||
*/
|
||||
public ResponseEntityResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
RequestedContentTypeResolver contentTypeResolver,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(messageWriters, contentTypeResolver, adapterRegistry);
|
||||
setOrder(0);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(HandlerResult result) {
|
||||
ResolvableType returnType = result.getReturnType();
|
||||
Class<?> returnType = result.getReturnType().getRawClass();
|
||||
if (isSupportedType(returnType)) {
|
||||
return true;
|
||||
}
|
||||
else if (getConversionService().canConvert(returnType.getRawClass(), Mono.class)) {
|
||||
else {
|
||||
Optional<Object> returnValue = result.getReturnValue();
|
||||
ReactiveAdapter adapter = getReactiveAdapterRegistry().getAdapterFrom(returnType, returnValue);
|
||||
if (adapter != null && !adapter.getDescriptor().isMultiValue()) {
|
||||
ResolvableType genericType = result.getReturnType().getGeneric(0);
|
||||
return isSupportedType(genericType);
|
||||
return isSupportedType(genericType.getRawClass());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isSupportedType(ResolvableType returnType) {
|
||||
Class<?> clazz = returnType.getRawClass();
|
||||
private boolean isSupportedType(Class<?> clazz) {
|
||||
return (HttpEntity.class.isAssignableFrom(clazz) && !RequestEntity.class.isAssignableFrom(clazz));
|
||||
}
|
||||
|
||||
|
|
@ -101,25 +104,24 @@ public class ResponseEntityResultHandler extends AbstractMessageWriterResultHand
|
|||
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
|
||||
|
||||
ResolvableType returnType = result.getReturnType();
|
||||
|
||||
ResolvableType bodyType;
|
||||
MethodParameter bodyTypeParameter;
|
||||
MethodParameter bodyType;
|
||||
|
||||
Mono<?> returnValueMono;
|
||||
Optional<Object> optional = result.getReturnValue();
|
||||
Optional<Object> optionalValue = result.getReturnValue();
|
||||
|
||||
if (optional.isPresent() && getConversionService().canConvert(returnType.getRawClass(), Mono.class)) {
|
||||
returnValueMono = getConversionService().convert(optional.get(), Mono.class);
|
||||
bodyType = returnType.getGeneric(0, 0);
|
||||
bodyTypeParameter = new MethodParameter(result.getReturnTypeSource());
|
||||
bodyTypeParameter.increaseNestingLevel();
|
||||
bodyTypeParameter.increaseNestingLevel();
|
||||
Class<?> rawClass = returnType.getRawClass();
|
||||
ReactiveAdapter adapter = getReactiveAdapterRegistry().getAdapterFrom(rawClass, optionalValue);
|
||||
|
||||
if (adapter != null) {
|
||||
returnValueMono = adapter.toMono(optionalValue);
|
||||
bodyType = new MethodParameter(result.getReturnTypeSource());
|
||||
bodyType.increaseNestingLevel();
|
||||
bodyType.increaseNestingLevel();
|
||||
}
|
||||
else {
|
||||
returnValueMono = Mono.justOrEmpty(optional);
|
||||
bodyType = returnType.getGeneric(0);
|
||||
bodyTypeParameter = new MethodParameter(result.getReturnTypeSource());
|
||||
bodyTypeParameter.increaseNestingLevel();
|
||||
returnValueMono = Mono.justOrEmpty(optionalValue);
|
||||
bodyType = new MethodParameter(result.getReturnTypeSource());
|
||||
bodyType.increaseNestingLevel();
|
||||
}
|
||||
|
||||
return returnValueMono.then(returnValue -> {
|
||||
|
|
@ -141,7 +143,7 @@ public class ResponseEntityResultHandler extends AbstractMessageWriterResultHand
|
|||
.forEach(entry -> responseHeaders.put(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
|
||||
return writeBody(exchange, httpEntity.getBody(), bodyType, bodyTypeParameter);
|
||||
return writeBody(httpEntity.getBody(), bodyType, exchange);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,16 +31,16 @@ import org.springframework.core.Conventions;
|
|||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
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;
|
||||
|
|
@ -84,26 +84,28 @@ public class ViewResolutionResultHandler extends ContentNegotiatingResultHandler
|
|||
|
||||
|
||||
/**
|
||||
* Constructor with {@code ViewResolver}s and a {@code ConversionService} only
|
||||
* and creating a {@link HeaderContentTypeResolver}, i.e. using Accept header
|
||||
* to determine the requested content type.
|
||||
* Constructor with {@link ViewResolver}s and a {@link RequestedContentTypeResolver}.
|
||||
* @param resolvers the resolver to use
|
||||
* @param conversionService for converting other reactive types (e.g. rx.Single) to Mono
|
||||
* @param contentTypeResolver for resolving the requested content type
|
||||
*/
|
||||
public ViewResolutionResultHandler(List<ViewResolver> resolvers, ConversionService conversionService) {
|
||||
this(resolvers, conversionService, new HeaderContentTypeResolver());
|
||||
public ViewResolutionResultHandler(List<ViewResolver> resolvers,
|
||||
RequestedContentTypeResolver contentTypeResolver) {
|
||||
|
||||
this(resolvers, contentTypeResolver, new ReactiveAdapterRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with {@code ViewResolver}s tand a {@code ConversionService}.
|
||||
* @param resolvers the resolver to use
|
||||
* @param conversionService for converting other reactive types (e.g. rx.Single) to Mono
|
||||
* @param contentTypeResolver for resolving the requested content type
|
||||
* @param adapterRegistry for adapting from other reactive types (e.g.
|
||||
* rx.Single) to Mono
|
||||
*/
|
||||
public ViewResolutionResultHandler(List<ViewResolver> resolvers, ConversionService conversionService,
|
||||
RequestedContentTypeResolver contentTypeResolver) {
|
||||
public ViewResolutionResultHandler(List<ViewResolver> resolvers,
|
||||
RequestedContentTypeResolver contentTypeResolver,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(conversionService, contentTypeResolver);
|
||||
super(contentTypeResolver, adapterRegistry);
|
||||
this.viewResolvers.addAll(resolvers);
|
||||
AnnotationAwareOrderComparator.sort(this.viewResolvers);
|
||||
}
|
||||
|
|
@ -143,7 +145,7 @@ public class ViewResolutionResultHandler extends ContentNegotiatingResultHandler
|
|||
if (isSupportedType(clazz)) {
|
||||
return true;
|
||||
}
|
||||
if (getConversionService().canConvert(clazz, Mono.class)) {
|
||||
if (getReactiveAdapterRegistry().getAdapterFrom(clazz, result.getReturnValue()) != null) {
|
||||
clazz = result.getReturnType().getGeneric(0).getRawClass();
|
||||
return isSupportedType(clazz);
|
||||
}
|
||||
|
|
@ -168,10 +170,12 @@ public class ViewResolutionResultHandler extends ContentNegotiatingResultHandler
|
|||
ResolvableType elementType;
|
||||
ResolvableType returnType = result.getReturnType();
|
||||
|
||||
if (getConversionService().canConvert(returnType.getRawClass(), Mono.class)) {
|
||||
Class<?> rawClass = returnType.getRawClass();
|
||||
Optional<Object> optionalValue = result.getReturnValue();
|
||||
ReactiveAdapter adapter = getReactiveAdapterRegistry().getAdapterFrom(rawClass, optionalValue);
|
||||
if (adapter != null) {
|
||||
if (optionalValue.isPresent()) {
|
||||
Mono<?> converted = getConversionService().convert(optionalValue.get(), Mono.class);
|
||||
Mono<?> converted = adapter.toMono(optionalValue);
|
||||
valueMono = converted.map(o -> o);
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.codec.StringEncoder;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
|
@ -43,6 +42,7 @@ import org.springframework.stereotype.Controller;
|
|||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.reactive.accept.HeaderContentTypeResolver;
|
||||
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;
|
||||
|
|
@ -198,7 +198,7 @@ public class DispatcherHandlerErrorTests {
|
|||
public ResponseBodyResultHandler resultHandler() {
|
||||
return new ResponseBodyResultHandler(
|
||||
Collections.singletonList(new EncoderHttpMessageWriter<>(new StringEncoder())),
|
||||
new DefaultConversionService());
|
||||
new HeaderContentTypeResolver());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import java.util.Set;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
|
|
@ -125,7 +124,7 @@ public class ContentNegotiatingResultHandlerSupportTests {
|
|||
}
|
||||
|
||||
public TestResultHandler(RequestedContentTypeResolver contentTypeResolver) {
|
||||
super(new GenericConversionService(), contentTypeResolver);
|
||||
super(contentTypeResolver);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,10 +27,6 @@ import rx.Observable;
|
|||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
|
||||
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
|
@ -47,10 +43,7 @@ public class SimpleResultHandlerTests {
|
|||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
this.resultHandler = new SimpleResultHandler(service);
|
||||
this.resultHandler = new SimpleResultHandler();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import reactor.core.publisher.Mono;
|
|||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
|
|
@ -147,7 +146,7 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler
|
|||
|
||||
@Bean
|
||||
public SimpleResultHandler resultHandler() {
|
||||
return new SimpleResultHandler(new DefaultConversionService());
|
||||
return new SimpleResultHandler();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,12 +35,8 @@ import rx.Single;
|
|||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
|
||||
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
|
|
@ -50,14 +46,20 @@ import org.springframework.http.converter.reactive.HttpMessageReader;
|
|||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.reactive.result.ResolvableMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebInputException;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.MockWebSessionManager;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.ResolvableType.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.core.ResolvableType.forClassWithGenerics;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link HttpEntityArgumentResolver}.When adding a test also
|
||||
|
|
@ -87,12 +89,7 @@ public class HttpEntityArgumentResolverTests {
|
|||
private HttpEntityArgumentResolver createResolver() {
|
||||
List<HttpMessageReader<?>> readers = new ArrayList<>();
|
||||
readers.add(new DecoderHttpMessageReader<>(new StringDecoder()));
|
||||
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
|
||||
return new HttpEntityArgumentResolver(readers, service);
|
||||
return new HttpEntityArgumentResolver(readers, mock(Validator.class));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -42,12 +42,8 @@ import rx.Single;
|
|||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
|
||||
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.json.JacksonJsonDecoder;
|
||||
|
|
@ -66,8 +62,12 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
|||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.MockWebSessionManager;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.ResolvableType.*;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.core.ResolvableType.forClass;
|
||||
import static org.springframework.core.ResolvableType.forClassWithGenerics;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AbstractMessageReaderArgumentResolver}.
|
||||
|
|
@ -275,15 +275,9 @@ public class MessageReaderArgumentResolverTests {
|
|||
|
||||
@SuppressWarnings("Convert2MethodRef")
|
||||
private AbstractMessageReaderArgumentResolver resolver(Decoder<?>... decoders) {
|
||||
|
||||
List<HttpMessageReader<?>> readers = new ArrayList<>();
|
||||
Arrays.asList(decoders).forEach(decoder -> readers.add(new DecoderHttpMessageReader<>(decoder)));
|
||||
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
|
||||
return new AbstractMessageReaderArgumentResolver(readers, service, new TestBeanValidator()) {};
|
||||
return new AbstractMessageReaderArgumentResolver(readers, new TestBeanValidator()) {};
|
||||
}
|
||||
|
||||
private DataBuffer dataBuffer(String body) {
|
||||
|
|
|
|||
|
|
@ -41,9 +41,6 @@ import org.springframework.core.MethodParameter;
|
|||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.ByteBufferEncoder;
|
||||
import org.springframework.core.codec.StringEncoder;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
|
||||
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
|
||||
|
|
@ -64,9 +61,11 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.MockWebSessionManager;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.http.MediaType.*;
|
||||
import static org.springframework.web.reactive.HandlerMapping.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
|
||||
import static org.springframework.web.reactive.HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AbstractMessageWriterResultHandler}.
|
||||
|
|
@ -93,7 +92,7 @@ public class MessageWriterResultHandlerTests {
|
|||
public void useDefaultContentType() throws Exception {
|
||||
Resource body = new ClassPathResource("logo.png", getClass());
|
||||
ResolvableType type = ResolvableType.forType(Resource.class);
|
||||
this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5));
|
||||
this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5));
|
||||
|
||||
assertEquals("image/x-png", this.response.getHeaders().getFirst("Content-Type"));
|
||||
}
|
||||
|
|
@ -105,7 +104,7 @@ public class MessageWriterResultHandlerTests {
|
|||
|
||||
String body = "foo";
|
||||
ResolvableType type = ResolvableType.forType(String.class);
|
||||
this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5));
|
||||
this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5));
|
||||
|
||||
assertEquals(APPLICATION_JSON_UTF8, this.response.getHeaders().getContentType());
|
||||
}
|
||||
|
|
@ -119,7 +118,7 @@ public class MessageWriterResultHandlerTests {
|
|||
}
|
||||
|
||||
private void testVoidReturnType(Object body, ResolvableType type) {
|
||||
this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5));
|
||||
this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5));
|
||||
|
||||
assertNull(this.response.getHeaders().get("Content-Type"));
|
||||
assertNull(this.response.getBody());
|
||||
|
|
@ -131,7 +130,7 @@ public class MessageWriterResultHandlerTests {
|
|||
ResolvableType type = ResolvableType.forType(OutputStream.class);
|
||||
|
||||
HttpMessageWriter<?> writer = new EncoderHttpMessageWriter<>(new ByteBufferEncoder());
|
||||
Mono<Void> mono = createResultHandler(writer).writeBody(this.exchange, body, type, returnType(type));
|
||||
Mono<Void> mono = createResultHandler(writer).writeBody(body, returnType(type), this.exchange);
|
||||
|
||||
TestSubscriber.subscribe(mono).assertError(IllegalStateException.class);
|
||||
}
|
||||
|
|
@ -140,7 +139,7 @@ public class MessageWriterResultHandlerTests {
|
|||
public void jacksonTypeOfListElement() throws Exception {
|
||||
List<ParentClass> body = Arrays.asList(new Foo("foo"), new Bar("bar"));
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(List.class, ParentClass.class);
|
||||
this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5));
|
||||
this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5));
|
||||
|
||||
assertEquals(APPLICATION_JSON_UTF8, this.response.getHeaders().getContentType());
|
||||
assertResponseBody("[{\"type\":\"foo\",\"parentProperty\":\"foo\"}," +
|
||||
|
|
@ -151,7 +150,7 @@ public class MessageWriterResultHandlerTests {
|
|||
public void jacksonTypeWithSubType() throws Exception {
|
||||
SimpleBean body = new SimpleBean(123L, "foo");
|
||||
ResolvableType type = ResolvableType.forClass(Identifiable.class);
|
||||
this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5));
|
||||
this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5));
|
||||
|
||||
assertEquals(APPLICATION_JSON_UTF8, this.response.getHeaders().getContentType());
|
||||
assertResponseBody("{\"id\":123,\"name\":\"foo\"}");
|
||||
|
|
@ -161,7 +160,7 @@ public class MessageWriterResultHandlerTests {
|
|||
public void jacksonTypeWithSubTypeOfListElement() throws Exception {
|
||||
List<SimpleBean> body = Arrays.asList(new SimpleBean(123L, "foo"), new SimpleBean(456L, "bar"));
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(List.class, Identifiable.class);
|
||||
this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5));
|
||||
this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5));
|
||||
|
||||
assertEquals(APPLICATION_JSON_UTF8, this.response.getHeaders().getContentType());
|
||||
assertResponseBody("[{\"id\":123,\"name\":\"foo\"},{\"id\":456,\"name\":\"bar\"}]");
|
||||
|
|
@ -185,14 +184,8 @@ public class MessageWriterResultHandlerTests {
|
|||
else {
|
||||
writerList = Arrays.asList(writers);
|
||||
}
|
||||
|
||||
GenericConversionService service = new GenericConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
|
||||
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
|
||||
|
||||
return new AbstractMessageWriterResultHandler(writerList, service, resolver) {};
|
||||
return new AbstractMessageWriterResultHandler(writerList, resolver) {};
|
||||
}
|
||||
|
||||
private void assertResponseBody(String responseBody) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package org.springframework.web.reactive.result.method.annotation;
|
|||
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -38,18 +37,15 @@ import rx.Single;
|
|||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
|
||||
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.converter.reactive.DecoderHttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.reactive.result.ResolvableMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
|
@ -62,6 +58,7 @@ import static org.junit.Assert.assertFalse;
|
|||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.core.ResolvableType.forClass;
|
||||
import static org.springframework.core.ResolvableType.forClassWithGenerics;
|
||||
|
||||
|
|
@ -93,12 +90,7 @@ public class RequestBodyArgumentResolverTests {
|
|||
private RequestBodyArgumentResolver resolver() {
|
||||
List<HttpMessageReader<?>> readers = new ArrayList<>();
|
||||
readers.add(new DecoderHttpMessageReader<>(new StringDecoder()));
|
||||
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
|
||||
return new RequestBodyArgumentResolver(readers, service);
|
||||
return new RequestBodyArgumentResolver(readers, mock(Validator.class));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,6 @@ import reactor.core.publisher.Mono;
|
|||
|
||||
import org.springframework.core.codec.ByteBufferEncoder;
|
||||
import org.springframework.core.codec.StringEncoder;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
|
||||
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
|
|
@ -99,12 +94,8 @@ public class ResponseBodyResultHandlerTests {
|
|||
else {
|
||||
writerList = Arrays.asList(writers);
|
||||
}
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
|
||||
|
||||
return new ResponseBodyResultHandler(writerList, new DefaultConversionService(), resolver);
|
||||
return new ResponseBodyResultHandler(writerList, resolver);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -34,11 +34,7 @@ import org.springframework.core.MethodParameter;
|
|||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.ByteBufferEncoder;
|
||||
import org.springframework.core.codec.StringEncoder;
|
||||
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
|
||||
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
|
||||
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
|
@ -59,8 +55,11 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.MockWebSessionManager;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.ResolvableType.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.core.ResolvableType.forClassWithGenerics;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ResponseEntityResultHandler}. When adding a test also
|
||||
|
|
@ -100,14 +99,8 @@ public class ResponseEntityResultHandlerTests {
|
|||
else {
|
||||
writerList = Arrays.asList(writers);
|
||||
}
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
|
||||
|
||||
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
|
||||
|
||||
return new ResponseEntityResultHandler(writerList, service, resolver);
|
||||
return new ResponseEntityResultHandler(writerList, resolver);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,13 +38,9 @@ import rx.Single;
|
|||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
|
||||
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
|
|
@ -55,6 +51,8 @@ import org.springframework.ui.Model;
|
|||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.accept.HeaderContentTypeResolver;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||
import org.springframework.web.reactive.result.ResolvableMethod;
|
||||
import org.springframework.web.server.NotAcceptableStatusException;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
|
@ -62,9 +60,10 @@ import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
|||
import org.springframework.web.server.session.DefaultWebSessionManager;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.http.MediaType.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ViewResolutionResultHandler}.
|
||||
|
|
@ -247,12 +246,9 @@ public class ViewResolutionResultHandlerTests {
|
|||
}
|
||||
|
||||
private ViewResolutionResultHandler createResultHandler(List<View> defaultViews, ViewResolver... resolvers) {
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
List<ViewResolver> resolverList = Arrays.asList(resolvers);
|
||||
|
||||
ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolverList, service);
|
||||
RequestedContentTypeResolver contentTypeResolver = new HeaderContentTypeResolver();
|
||||
ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolverList, contentTypeResolver);
|
||||
handler.setDefaultViews(defaultViews);
|
||||
return handler;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue