WebHook是一种基于HTTP的回调机制,它允许服务之间通过发布/订阅模型进行通信,而无需服务之间进行周期性的API调用。当服务中发生事件时,会以HTTP POST请求的形式向注册的订阅者发送通知,POST请求中包含了事件的相关信息,使得接收者能够据此采取相应的行动。
WebHook采用发布-订阅消息模式,消息的发送者称为发布者(publishers),接收者称为订阅者(subscribers)。WebHook的主要优势在于,应用程序不需要周期性地调用API,相反,API会在特定端点调用应用程序,通知发生了某些有趣的事件。
在项目中实现自定义WebHook的需求促使深入了解了WebHook的实现方式。在一个ASP.NET MVC5.0的项目中实现了自定义WebHook。由于项目需求,需要在当前的代码库中实现自定义WebHook。幸运的是,Microsoft提供了一个名为Microsoft.AspNet.WebHooks.Custom.dll的库,它极大地简化了工作。
在实现过程中,遇到了一个问题:在演示服务器上,将WebHook信息存储在内存中是可行的,但在生产环境中,需要将WebHook信息存储在持久化存储中。由于应用程序将部署在不同的服务器上,不能使用Azure表存储。因此,需要找到一种方法来实现这一目标。
在编写本文时,得知有一个更新,即Microsoft.AspNet.WebHooks.Custom.SqlStorage.dll,它允许将WebHook信息存储在SQL Server中。但在实现时,这个库尚未发布,因此需要自己编写机制来实现持久化存储。
通过实现一个接口IWebHookStore来管理WebHook订阅,这个接口提供了查询、插入、更新和删除订阅的抽象。默认的IWebHookStore实现仅在内存中进行操作。想法是编写一个类来实现IWebHookStore接口,并将依赖项传递给WebHookManager。
以下是创建的持久化存储类的示例代码,并将其实例传递给WebHookManager:
C#
IWebHookStore _whStore = new PersistentWebHookStore();
IWebHookManager _whManager = new WebHookManager(_whStore, new TraceLogger());
接下来,将编写PersistentWebHookStore类并实现Microsoft.AspNet.WebHooks中的IWebHookStore接口。
C#
namespace Microsoft.AspNet.WebHooks
{
public interface IWebHookStore
{
Task DeleteAllWebHooksAsync(string user);
Task<StoreResult> DeleteWebHookAsync(string user, string id);
Task<ICollection<WebHook>> GetAllWebHooksAsync(string user);
Task<StoreResult> InsertWebHookAsync(string user, WebHook webHook);
Task<WebHook> LookupWebHookAsync(string user, string id);
Task<ICollection<WebHook>> QueryWebHooksAsync(string user, IEnumerable<string> actions);
Task<StoreResult> UpdateWebHookAsync(string user, WebHook webHook);
}
}
PersistentWebHookStore类实现了IWebHookStore接口,提供了持久化存储WebHook的方法。以下是PersistentWebHookStore类的一部分实现:
C#
public class PersistentWebHookStore : IWebHookStore
{
#region Member Variables
private readonly IWebHookRepository _webHookRepository = null;
#endregion
#region Constructor
public PersistentWebHookStore()
{
_webHookRepository = new WebHookRepository();
}
#endregion
#region IWebHookStore Methods
public Task<StoreResult> InsertWebHookAsync(string user, WebHook webHook)
{
if (!string.IsNullOrWhiteSpace(user) && webHook != null)
{
user = Normalize(user);
bool isInserted = _webHookRepository.Insert(user, webHook);
StoreResult result = isInserted ? StoreResult.Success : StoreResult.Conflict;
return Task.FromResult(result);
}
else
{
throw new Exception("Values expected for either user or webHook");
}
}
// ... 其他方法的实现 ...
#endregion
}