Add WebResponseExtractor impl for RxJava1
This commit adds `RxJava1WebResponseExtractors`, a static factory of `WebResponseExtractor`s that are based on the RxJava1 composition API. All extracted types are based on the `Single` and `Observable` types.
This commit is contained in:
parent
69ce33e1ee
commit
7ab92cfccf
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2016 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.client.reactive;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import reactor.core.converter.RxJava1ObservableConverter;
|
||||||
|
import reactor.core.converter.RxJava1SingleConverter;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import rx.Observable;
|
||||||
|
import rx.Single;
|
||||||
|
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
|
import org.springframework.core.codec.Decoder;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static factory methods for {@link WebResponseExtractor}
|
||||||
|
* based on the {@link Observable} and {@link Single} API.
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
*/
|
||||||
|
public class RxJava1WebResponseExtractors {
|
||||||
|
|
||||||
|
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||||
|
|
||||||
|
private static final Object[] HINTS = new Object[] {UTF_8};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the response body and decode it, returning it as a {@code Single<T>}
|
||||||
|
*/
|
||||||
|
public static <T> WebResponseExtractor<Single<T>> body(Class<T> sourceClass) {
|
||||||
|
|
||||||
|
ResolvableType resolvableType = ResolvableType.forClass(sourceClass);
|
||||||
|
//noinspection unchecked
|
||||||
|
return webResponse -> (Single<T>) RxJava1SingleConverter.from(webResponse.getClientResponse()
|
||||||
|
.flatMap(resp -> decodeResponseBody(resp, resolvableType, webResponse.getMessageDecoders()))
|
||||||
|
.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the response body and decode it, returning it as an {@code Observable<T>}
|
||||||
|
*/
|
||||||
|
public static <T> WebResponseExtractor<Observable<T>> bodyStream(Class<T> sourceClass) {
|
||||||
|
|
||||||
|
ResolvableType resolvableType = ResolvableType.forClass(sourceClass);
|
||||||
|
return webResponse -> RxJava1ObservableConverter.from(webResponse.getClientResponse()
|
||||||
|
.flatMap(resp -> decodeResponseBody(resp, resolvableType, webResponse.getMessageDecoders())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the full response body as a {@code ResponseEntity}
|
||||||
|
* with its body decoded as a single type {@code T}
|
||||||
|
*/
|
||||||
|
public static <T> WebResponseExtractor<Single<ResponseEntity<T>>> response(Class<T> sourceClass) {
|
||||||
|
|
||||||
|
ResolvableType resolvableType = ResolvableType.forClass(sourceClass);
|
||||||
|
return webResponse -> (Single<ResponseEntity<T>>)
|
||||||
|
RxJava1SingleConverter.from(webResponse.getClientResponse()
|
||||||
|
.then(response ->
|
||||||
|
Mono.when(
|
||||||
|
decodeResponseBody(response, resolvableType, webResponse.getMessageDecoders()).next(),
|
||||||
|
Mono.just(response.getHeaders()),
|
||||||
|
Mono.just(response.getStatusCode())))
|
||||||
|
.map(tuple -> {
|
||||||
|
//noinspection unchecked
|
||||||
|
return new ResponseEntity<>((T) tuple.getT1(), tuple.getT2(), tuple.getT3());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the full response body as a {@code ResponseEntity}
|
||||||
|
* with its body decoded as an {@code Observable<T>}
|
||||||
|
*/
|
||||||
|
public static <T> WebResponseExtractor<Single<ResponseEntity<Observable<T>>>> responseStream(Class<T> sourceClass) {
|
||||||
|
ResolvableType resolvableType = ResolvableType.forClass(sourceClass);
|
||||||
|
return webResponse -> RxJava1SingleConverter.from(webResponse.getClientResponse()
|
||||||
|
.map(response -> new ResponseEntity<>(
|
||||||
|
RxJava1ObservableConverter
|
||||||
|
.from(decodeResponseBody(response, resolvableType, webResponse.getMessageDecoders())),
|
||||||
|
response.getHeaders(),
|
||||||
|
response.getStatusCode())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the response headers as an {@code HttpHeaders} instance
|
||||||
|
*/
|
||||||
|
public static WebResponseExtractor<Single<HttpHeaders>> headers() {
|
||||||
|
return webResponse -> RxJava1SingleConverter
|
||||||
|
.from(webResponse.getClientResponse().map(resp -> resp.getHeaders()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static <T> Flux<T> decodeResponseBody(ClientHttpResponse response, ResolvableType responseType,
|
||||||
|
List<Decoder<?>> messageDecoders) {
|
||||||
|
|
||||||
|
MediaType contentType = response.getHeaders().getContentType();
|
||||||
|
Optional<Decoder<?>> decoder = resolveDecoder(messageDecoders, responseType, contentType);
|
||||||
|
if (!decoder.isPresent()) {
|
||||||
|
return Flux.error(new IllegalStateException("Could not decode response body of type '" + contentType +
|
||||||
|
"' with target type '" + responseType.toString() + "'"));
|
||||||
|
}
|
||||||
|
//noinspection unchecked
|
||||||
|
return (Flux<T>) decoder.get().decode(response.getBody(), responseType, contentType, HINTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected static Optional<Decoder<?>> resolveDecoder(List<Decoder<?>> messageDecoders, ResolvableType type,
|
||||||
|
MediaType mediaType) {
|
||||||
|
return messageDecoders.stream().filter(e -> e.canDecode(type, mediaType)).findFirst();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue