IObservable REST 客户端

IObservable REST Client

提问人:Tomáš Bezouška 提问时间:8/14/2012 更新时间:8/15/2012 访问量:1479

问:

我想编写一个库,该库可以与 Web 服务器通信,并将来自它的数据公开给世界其他地方。Web 服务器没什么特别的,它公开了几种 REST 方法,主要是 GET 和 POST。

由于我对 Reactive Extensions 比较陌生(但我已经喜欢它了),所以我寻求建议。我决定库的接口将公开 IObservables。但我不知道如何具体实现这一点。我想我有几个选择:

1)曝光。REST服务一次返回所有请求的数据是有道理的。用户调用 Subscribe(),只推送一个 IEnumerable,调用 OnDone。因此,需要多次调用 Subscribe()。IObservable<IEnumerable<T>>

2)曝光。我想在某些情况下可能是一个不错的选择。Subscribe() 只会被调用一次,要获取其他数据,会有 Refresh() 或 NextPage() (...) 方法将更多数据获取到流中。(那么它可能不是一个属性,IObservable<T>IObservable<T> GetResource...IObservable<T> Resource { get; }

3)忘记Rx,通过事件以老式的方式做(最糟糕的事情IMO)

4) 其他方式?

有这方面的经验吗?我关心的是刷新(要求新数据)、分页、组合结果以及通常具有良好的可维护设计。

谢谢你的任何建议

C# .NET REST 客户端 System.Reactive

评论


答:

0赞 casperOne 8/14/2012 #1

你在这里结合了两个问题,确实应该分开解决。

首先是你的代码从其他来源获取数据。第二种方法是在有新数据可用时将该数据发布给相关方。

关于第一个,反应式扩展无济于事。您在这里关心的是按时间间隔获取数据;它必须是一个定时间隔,因为在代码中调用 REST 服务时,没有回调,没有服务可以调用的挂钩。

如果从外部服务对代码进行了某种回调,则可以将其包装在一些 IObservable<T> 实现中,然后您可以订阅并对其执行操作(实际上只是转发订阅)。

将反应式扩展用于第一个问题的唯一方法是在 Observable上使用静态 Timer 方法启动计时器。

对于第二个问题(在您拥有数据并想要通知订阅者之后),您绝对可以并且应该使用实现来通知订阅者。IObservable<T>

在这种情况下,我强烈建议您不要尝试偏离接口上 Subscribe 方法的意图。您应该公开一个方法,该方法将为您提供任何人都可以订阅的方法(在调用之前是热的还是冷的由您决定),并通过调用返回的 IDisposable 接口实现上的 Dispose 方法取消订阅。IObservable<T>IObservable<T>IObservable<T>SubscribeSubscribe

这样,您的客户就可以获取 ,订阅他们想要的通知,然后在完成后取消订阅。IObservable<T>

评论

0赞 Tomáš Bezouška 8/15/2012
关于 Rx 下载东西没有用 - 有一个场景我确实发现它很有用 - 一次获取多个资源(使用多个 rest 请求),然后通知完成。ForkJoin 显然是这里的赢家。你觉得怎么样?
0赞 casperOne 8/15/2012
@TomášBezouška 我并没有说下载东西没有用,而是说,最好暴露你正在下载的内容。大多数网络库不返回实现,因此您通常没有这种奢侈,并且您没有在问题中表明您这样做了。IObservable<T>
2赞 Enigmativity 8/14/2012 #2

我建议使用以下界面:

public interface IRestService
{
    IObservable<T> GetResources<T>();
}

这种选择背后有很多原因。

公开 将反应式与交互式(或可观察对象与枚举对象)混合在一起,并会强制查询调用,或者更糟糕的是,构造基本查询。最好更好地保持查询和订阅代码的干净。IObservable<IEnumerable<T>>.ToObservable().ToEnumerable()SelectMany

现在,您建议使用 an 您只需订阅一次,并且需要 or 调用才能将更多数据获取到流中。这不是一个好主意。相反,您应该认为单个订阅将返回单个 REST 调用的所有结果,然后调用 .如果要调用新的 REST 调用,只需再次订阅即可。IObservable<T>RefreshNextPageOnComplete

此外,代码中未明确表达单个订阅调用的语义。因此,您需要考虑维护代码。当你将来查看代码时,你可能会认为语义是什么?我建议单个订阅的语义更有可能映射到单个 REST 调用。否则,您的代码可能会更加混乱。

更进一步,您应该避免使用单个订阅模型,因为如果抛出任何异常,那么您的可观察对象就完成了,当然,调用 Web 服务可能非常容易出错。如果多订阅模型出现错误,则可以更轻松地恢复。

我也会避免,因为它建议某种“固定”值,相反,它更动态 - 换句话说,每个调用可能会给你不同的值。最好调用方法而不是属性。IObservable<T> Resources { get; }GetResourcesResources

一些,底线,我会有一个抽象到你的底层REST服务的单个调用。IObservable<T>

评论

0赞 Tomáš Bezouška 8/15/2012
是的,但是 Rx 的整个想法不是只有一个订阅并将数据推送给调用者吗?每次都必须订阅才能获得结果,感觉不是很被动。
0赞 Enigmativity 8/15/2012
@TomášBezouška - 我认为你错过了关于可组合性的重点。你可能会有一个按钮单击或一个计时器来启动对 Web 服务的调用,因此你只需将它们转换为可观察对象,然后创建一个查询,以你喜欢的任何方式将所有内容组合在一起。重要的部分是每个部分执行一个基本操作,然后整个查询就可以强大了。这有道理吗?
0赞 Tomáš Bezouška 8/16/2012
好的,使用 Refresh() 等方法会破坏这种可组合性,对吧?我想我明白了,谢谢你的澄清。
0赞 Enigmativity 8/16/2012
@TomášBezouška - 当然可以有一个刷新方法,将一个发送到一个主题,该主题是触发对 Web 服务的调用的查询的一部分。您可以使用可组合性使它变得非常实用和富有表现力。.OnNext(Default.Unit)