我想从春天的mvc控制器返回Observable
。它适用于Single
:
@GetMapping Object a() {return Single.just(1);}
如预期的我得到1
时,我查询服务器。但是当我对Observable
做同样的事情时:
@GetMapping Object a() {return Observable.just(1);}
我得到的答案是{}
。spring-mvc不订阅返回的Observable
,而是简单地将其序列化为json。spring-mvc可以理解Observable
开箱即用,我只是搞砸了一些配置?或者我必须注册自定义处理程序或安装一些插件吗?
你可以使用Spring MVC Reactive(但它目前不是最终版本)。它与Reactor和RxJava一起工作。您将能够编写这样的控制器:
@Test
class ExampleController {
@RequestMapping("/hello")
public Single<String> hello() { return Single.just("world"); }
}
或者您可以编写自己的类适配器并将Single
转换为Spring DeferredResult
(参见此示例)
这个例子来自一个Spring Boot Starter,你可能想直接使用它
spring-cloud-netflix
中有一个返回值处理程序。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-core</artifactId>
</dependency>
我用rx 1.x
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<version>1.1.10</version>
</dependency>
如果你不能升级到spring-reactive,你可以使用org.springframework.web.context.request.async.DeferredResult
public class ExampleController {
@RequestMapping("/hello")
public DeferredResult<ResponseEntity<String>> hello() {
DeferredResult<ResponseEntity<String>> deferredResult = new DeferredResult<>();
// your observable
Observable.just("world")
.subscribe(
text -> deferredResult.setResult(
ResponseEntity.accepted()
.contentType(MediaType.TEXT_PLAIN)
.body(text)),
error -> deferredResult.setResult(
ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT)
.build()
);
return deferredResult;
}
}
如果您使用的是springboot 1.5并希望从你的restControllers返回Observable避免这样的信息:"没有找到类型为:class io.reactivex.internal.operators.observable.ObservableMap的返回值的转换器"你必须添加两个类
@EnableWebMvc
@Configuration
@ComponentScan(basePackages = {"PACKAGE CONTROLLER"})
public class DispatcherContextConfig extends WebMvcConfigurerAdapter {
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
returnValueHandlers.add(new ObservableReturnValueHandler());
}
}
和
public class ObservableReturnValueHandler implements AsyncHandlerMethodReturnValueHandler {
@Override
public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) {
return returnValue != null && supportsReturnType(returnType);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return Observable.class.isAssignableFrom(returnType.getParameterType());
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
mavContainer.setRequestHandled(true);
return;
}
final Observable<?> observable = Observable.class.cast(returnValue);
WebAsyncUtils.getAsyncManager(webRequest)
.startDeferredResultProcessing(new ObservableAdapter<>(observable), mavContainer);
}
public class ObservableAdapter<T> extends DeferredResult<T> {
public ObservableAdapter(Observable<T> observable) {
observable.subscribe(this::setResult, this::setErrorResult);
}
}
}
然后 @GetMapping(path = "{idDocument}/ropObs")
public Observable<DocumentDto> getDocumentRopObs(@PathVariable String idDocument) {
DocumentDto r = documentService.getDocumentRopInfo(idDocument)
.map(dtoMapper::documentBusinessToDto)
.doOnError(error -> {throw new ApiErrorServerError(error.getMessage());})
.blockingSingle();
return Observable.just(r);
}
和更好的实践
@GetMapping(path = "{idDocument}/ropObs2")
public Observable<DocumentDto> getDocumentRopObs2(@PathVariable String idDocument) {
return documentService.getDocumentRopInfo(idDocument).map(dtoMapper::documentBusinessToDto);
}