我需要创建一个连接到第三方SOAP API的REST API。第三方API事件通过回调发送到我提供的URL。
我的API经历的典型步骤是通过提供ID和回调URL来启动与第三方的会话。第三方现在可以通过此URL向我的API发送新事件,例如,当新参与者连接时。现在有时我需要请求特定信息,例如给定会话(ID)的参与者列表,并等待包含信息的事件。请注意,可能同时有多个打开的会话。
我需要的一个例子:
private string url = "http://myapi/callback";
[HttpGet]
[Route("createSession")]
public async Task<string> CreateSession()
{
var id = Guid.NewGuid().ToString();
var result = await ExternAPI.CreateSession(id, this.url);
return result; //contains the id
}
[HttpGet]
[Route("endSession")]
public async Task<string> EndSession([FromUri] string id)
{
var result = await ExternAPI.EndSession(id);
return result;
}
[HttpGet]
[Route("partipants")]
public async Task<string> Partipants([FromUri] string id)
{
ExternAPI.participants(id); // The results of this method will be sent to the callback function
results = // Wait for the results for this id
return results;
}
[HttpPost]
[Route("callback")]
public void Callback(body)
{
// notify waiting function and pass body
}
我想出了一个使用ReactiveX的解决方案,但我不确定它在生产中的可靠性。我想到的是创建一个永远不会终止和处理所有事件的主题,但它不是主题的通常生命周期,错误会发生什么?我不认为我是“RX-way”(国家担忧)。
这是(您将需要System.Reactive来运行此代码):
class Data
{
public int id;
public string value;
}
class Program
{
private static Subject<Data> sub;
static void Main(string[] args)
{
sub = new Subject<Data>();
Task.Run(async () => {
int id = 1;
ExternAPI(CallBackHook, id);
Data result = await sub.Where(data => data.id == id).FirstAsync();
Console.WriteLine("{0}", result.value);
});
Console.ReadLine();
}
static void CallBackHook(Data data)
{
sub.OnNext(data);
}
static String ExternAPI(Action<Data> callback, int id)
{
// Third-party API, access via SOAP. callback is normally an url (string)
Task.Run(() =>
{
Thread.Sleep(1000);
callback(new Data { id = id, value = "test" });
});
return "success";
}
}
另一种方式是主题词典,每个会话一个,所以我可以管理他们的一生。
这不是一个主题的通常生命
错误会发生什么?
我不认为我是“RX-way”
是的,这些都是这种方法的完全有效的关注点。就个人而言,我并不介意最后一个,因为即使主题是皱眉,很多时候它们比正确的Rx方式更容易使用。随着Rx的学习曲线是什么,我倾向于优化开发人员的可维护性,所以我做“欺骗”并使用主题,除非替代方案同样可以理解。
关于生命周期和错误,那里的解决方案取决于您希望应用程序的行为方式。
对于生命周期,看起来目前您有一个WebAPI资源(SOAP连接)需要来自客户端的显式断开连接调用;这引起了一些危险信号。至少,你需要某种超时,即使从未调用过endSession
,该资源也会被处理掉。否则,结束悬空资源将太容易了。
另外,对于错误,您需要确定适当的方法。您可以“缓存”错误并将其报告给尝试使用该资源的每个调用,并在调用endSession
时“清除”错误。或者,如果它更合适,您可以让错误消除您的ASP.NET进程。 (ASP.NET将为您重新启动一个新的)。
要延迟API直到您获得其他事件,请使用TaskCompletionSource<T>
。在启动SOAP调用(例如,ExternAPI.participants
)时,您应该创建一个新的TCS<T>
。然后API调用await
TaskCompletionSource<T>.Task
。当SOAP服务响应事件时,应该使用TaskCompletionSource<T>
并完成它。注意事项:
TaskCompletionSource<T>
实例,以及某种消息标识符,以匹配哪些事件用于哪些调用。TaskCompletionSource<T>
本身就是线程安全的,但你也需要让你的集合线程安全。您可能希望首先为SOAP服务编写基于Task
的包装器(处理所有TaskCompletionSource<T>
的东西),然后从WebAPI中使用它。
作为一个非常广泛的替代方案,我不会考虑使用WebAPI桥接SOAP,而是考虑使用SignalR桥接SOAP。您可能会发现这是一种更自然的翻译。除此之外,SignalR还将为您提供客户端连接和客户端断开事件(完成客户端的内置超时)。这样可以更自然地解决您的终身问题。您也可以为SOAP服务使用相同的基于Task
的包装器,或者直接将SOAP事件作为SignalR消息公开。