受到T.Nurkiewicz"反应性编程与RxJava的启发"我试图在我正在进行的项目中应用它,这是我面临的问题。
我有一个Rest端点,它接受输入流和用户名,并返回更新用户名的链接或返回错误请求错误。以下是我尝试使用RxJava实现此目的的方法:
@PUT
@Path("{username}")
public Response updateCredential(@PathParam("username") final String username, InputStream stream) {
CredentialCandidate candidate = new CredentialCandidate();
Observable.just(repository.getByUsername(username))
.subscribe(
credential -> {
serializeCandidate(candidate, stream);
try {
repository.updateCredential(build(credential, candidate));
} catch (Exception e) {
String msg = "Failed to update credential +\""+username+"\": "+e.getMessage();
throw new BadRequestException(msg, Response.status(Response.Status.BAD_REQUEST).build());
}
},
ex -> {
String msg = "Couldn't update credential \""+username+"\""
+ ". A credential with such username doesn't exist: " + ex.getMessage();
logger.error(msg);
throw new BadRequestException(msg, Response.status(Response.Status.BAD_REQUEST).build());
});//if the Observable completes without exceptions we have a success case
Map<String, String> map = new HashMap<>();
map.put("path", "credential/" + username);
return Response.ok(getJsonRepr("link", uriGenerator.apply(appsUriBuilder, map).toASCIIString())).build();
}
我的问题出在第11行(onNext
方法的catch子句)。这是日志输出,可以快速显示发生的情况:
19:23:50.472 [http-listener(4)] ERROR com.vgorcinschi.rimmanew.rest.services.CredentialResourceService - Couldn't update credential "admin". A credential with such username doesn't exist: Failed to update credential +"admin": Password too weak!
因此,onNext
方法中抛出的异常进入上游并以onError
方法结束!显然this works as designed,但我对如何返回错误请求错误的正确原因感到困惑。毕竟在我的测试用例中,存储库找到了用户的凭证,正确的错误是建议的密码太弱了。这是生成错误的辅助方法:
private Credential build(Credential credential, CredentialCandidate candidate) {
if(!isOkPsswd.test(candidate.getPassword())){
throw new BadRequestException("Password too weak!", Response.status(Response.Status.BAD_REQUEST).build());
}
...
}
我仍然是反应式编程的新手,所以我意识到我可能会遗漏一些显而易见的东西。浏览这本书并没有让我得到答案,所以我将不胜感激。
以防万一,这是完整的堆栈跟踪:
updateCredentialTest(com.vgorcinschi.rimmanew.services.CredentialResourceServiceTest) Time elapsed: 0.798 sec <<< ERROR!
rx.exceptions.OnErrorFailedException: Error occurred when trying to propagate error to Observer.onError
at com.vgorcinschi.rimmanew.rest.services.CredentialResourceService.lambda$updateCredential$9(CredentialResourceService.java:245)
at rx.internal.util.ActionSubscriber.onNext(ActionSubscriber.java:39)
at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:134)
at rx.internal.util.ScalarSynchronousObservable$WeakSingleProducer.request(ScalarSynchronousObservable.java:276)
at rx.Subscriber.setProducer(Subscriber.java:209)
at rx.Subscriber.setProducer(Subscriber.java:205)
at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:138)
at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:129)
at rx.Observable.subscribe(Observable.java:10238)
at rx.Observable.subscribe(Observable.java:10205)
at rx.Observable.subscribe(Observable.java:10045)
at com.vgorcinschi.rimmanew.rest.services.CredentialResourceService.updateCredential(CredentialResourceService.java:238)
at com.vgorcinschi.rimmanew.services.CredentialResourceServiceTest.updateCredentialTest(CredentialResourceServiceTest.java:140)
答案 0 :(得分:3)
似乎你没有正确掌握反应式编程原则。
首先,Observable
的API是异步的,而您试图通过尝试直接从方法返回Response
值而不是返回{来强制它成为同步API {1}} Observable<Response>
通过Response
通知随时间发出onNext()
值
这就是为什么你正在努力解决异常,每个通知lambda方法(onNext
/ onError
)都由Observable
机制封装,以便创建一个遵守某些规则的正确流( Observable contract),其中一些预期的行为是错误应该重定向到onError()
方法,这是异常捕获方法,你不应该扔到那里,扔在那里将被视为致命错误并且会被投掷OnErrorFailedException
吞下。
理想情况下会是这样的:
public Observable<Response> updateCredential(@PathParam("username") final String username,
InputStream stream) {
rerurn Observable.fromCallable(() -> {
CredentialCandidate candidate = new CredentialCandidate();
Credential credential = repository.getByUsername(username);
serializeCandidate(candidate, stream);
repository.updateCredential(build(credential, candidate));
Map<String, String> map = new HashMap<>();
map.put("path", "credential/" + username);
return Response.ok(getJsonRepr("link", uriGenerator.apply(appsUriBuilder, map).toASCIIString())).build();
})
.onErrorReturn(throwable -> {
String msg = "Failed to update credential +\"" + username + "\": " + e.getMessage();
throw new BadRequestException(msg, Response.status(Response.Status.BAD_REQUEST).build());
});
}
使用fromCallable
以便在订阅时使请求发生(当Observable.just(repository.getByUsername(username))
在Observable构造时将同步执行),成功路径与可调用本身有关,而如果发生任何错误,您将使用onErrorReturn
运算符将其转换为自定义异常。
通过他的方法,您将返回Observable
对象,当您订阅它时,该对象将起作用,您将获得Observable
和Reactive方法的所有好处,以便能够将其与其他一些操作组合在一起,能够从外部指定它是同步行动(当前线程)还是异步行动(使用Scheduler
)。
关于反应式编程的更详细解释,我建议从AndréStaltz的这个伟大的tutorial开始。