等待第三方API回调

时间:2019-03-05 20:04:56

标签: c# .net asp.net-web-api2 reactive-programming system.reactive

我需要创建一个连接到第三方SOAP API的REST API。第三方API事件通过回调发送到我提供的URL。

我的API通过的典型步骤是通过提供ID和回调URL来与第三方进行会话。现在,例如当有新参与者连接时,第三方可以通过此URL将新事件发送到我的API。现在有时我需要请求特定信息,例如给定session(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方式”(出于国家考虑)。

在这里(您需要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";
    }
}

另一种方式是字典主题,每节课一个,因此我可以管理它们的寿命。

1 个答案:

答案 0 :(得分:0)

  

这不是一个受试者的一生

     

发生错误时会发生什么?

     

我不认为我是通过“ RX方式”完成的

是的,这些都是使用这种方法的完全有效的关注点。就个人而言,我不太在乎最后一个,因为即使主题被皱眉了,很多时候它们也比正确的Rx方法更容易使用。借助Rx的学习曲线,我倾向于针对开发人员的可维护性进行优化,因此我会“作弊”并使用Subjects,除非同样可以理解该替代方案。

关于生存期和错误,那里的解决方案取决于您希望应用程序如何运行。

在整个生命周期中,看起来您当前拥有一个WebAPI资源(SOAP连接),需要从客户端显式断开连接;这引起了一些危险信号。至少,您希望在那里存在某种超时,即使从未调用endSession,该资源也会在那里处置。否则,最终很容易陷入资源悬空的情况。

对于错误,您还需要确定适当的方法。您可以“缓存”错误并将其报告给每个尝试使用该资源的调用,并在调用endSession时“清除”错误。或者,如果更合适,则可以让错误减少您的ASP.NET进程。 (ASP.NET将为您重新启动一个新版本。)

要延迟API直到发生其他事件,请使用TaskCompletionSource<T>。启动SOAP调用(例如ExternAPI.participants)时,您应该创建一个新的TCS<T>。然后,API调用应await TaskCompletionSource<T>.Task。当SOAP服务响应事件时,它应该使用TaskCompletionSource<T>并完成它。注意事项:

  • 如果您有多个SOAP调用期望对同一事件进行响应,那么您将需要TaskCompletionSource<T>实例的 collection 以及某种消息标识符来进行匹配哪些事件适合哪些电话。
  • 请务必注意线程安全。传入的SOAP事件很可能到达线程池,而其他线程池线程上的API请求(可能是多个)。 TaskCompletionSource<T>本身是线程安全的,但是您还需要使集合的线程安全。

您可能想先为SOAP服务编写一个基于Task的包装器(处理所有TaskCompletionSource<T>东西),然后从WebAPI中使用那个。 / p>

作为一个非常广泛的替代方案,我将考虑使用SignalR桥接SOAP,而不是使用WebAPI桥接SOAP。您可能会发现这是更自然的翻译。除其他外,SignalR将为您提供客户端连接和客户端断开事件(客户端内置的超时功能)。这样可以更自然地解决您的一生问题。您也可以对您的SOAP服务使用相同的基于Task的包装器,也可以直接将SOAP事件作为SignalR消息公开。