我有一个API、一个C#客户端及其API包装器。我有一个包含扩展方法的类,可以灵活地使用API。
我现在正在努力处理错误。其中一种扩展方法是:
public static IObservable<HttpResponseMessage> CheckForApiErrors(this IObservable<HttpResponseMessage> source)
{
return source.SelectMany(message => {
if (message.IsSuccessStatusCode) {
return source;
}
var exTask = ApiException.CreateFromHttpMessage(message);
return Observable.FromAsync(() => exTask).SelectMany(Observable.Throw<HttpResponseMessage>);
});
}
这投对了,我可以稍后在可观察的上.Catch()
。但是,所有通过该方法(并且成功)的请求现在都会重播。如果我调用/some_endpoint
并且它成功了,那么可观察到的被重放并且/some_endpoint
被再次调用。
我认为故障线路在这里:
return source.SelectMany(message => {
if (message.IsSuccessStatusCode) {
return source; // <-----------
}
我真的不知道如何抛出correclity和error,我怀疑我的SelectMany方法来检查错误和抛出,但这是我找到的唯一解决方案。
我希望Do()
中的throw
不会干扰正常的流,但这会导致整个应用程序崩溃,而不仅仅是可观察到的。
编辑(开了个会,中间有个主意,今天早上一定很累):
代替return source;
,我可以做return Observable.Return(message);
。这当然有效。但我仍然不太喜欢我的方法,我仍然觉得有一种更优雅的方法可以实现这一点。
我假设ApiException
的签名大致如下?
public class ApiException
{
public static Task<Exception> CreateFromHttpMessage(HttpResponseMessage message)
{
return Task.FromResult(new Exception());
}
}
如果您返回Exception
而不是Task<Exception>
,则会更直接。无论如何,这里有一些替代方案:
public static IObservable<HttpResponseMessage> CheckForApiErrors2(this IObservable<HttpResponseMessage> source)
{
return source.SelectMany(message => message.IsSuccessStatusCode
? Observable.Return(message)
: Observable.FromAsync(() => ApiException.CreateFromHttpMessage(message)).SelectMany(Observable.Throw<HttpResponseMessage>
)
}
public static IObservable<HttpResponseMessage> CheckForApiErrors3(this IObservable<HttpResponseMessage> source)
{
return source.Publish(_source => _source
.Where(message => message.IsSuccessStatusCode)
.Merge(_source
.Where(message => !message.IsSuccessStatusCode)
.SelectMany(message => Observable.FromAsync(() => ApiException.CreateFromHttpMessage(message)).SelectMany(Observable.Throw<HttpResponseMessage>))
)
);
}
第一种选择与您的代码基本相同,但经过了压缩。第二种方法将代码分成两个流(成功和失败),分别处理它们,然后将它们合并在一起。