{"id":6082,"date":"2015-12-31T06:54:43","date_gmt":"2015-12-31T06:54:43","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/webdev\/2015\/12\/31\/new-year-updates-to-asp-net-webhooks-preview\/"},"modified":"2023-09-21T11:33:52","modified_gmt":"2023-09-21T18:33:52","slug":"new-year-updates-to-asp-net-webhooks-preview","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/new-year-updates-to-asp-net-webhooks-preview\/","title":{"rendered":"New Year Updates to ASP.NET WebHooks Preview"},"content":{"rendered":"<p>We just released a new update of ASP.NET WebHooks with a couple of interesting new features on the WebHooks <em>sender<\/em> side \u2013 that is, when you want to send WebHooks to others when some event happens \u2013 including:<\/p>\n<ol>\n<li>Sending events to <em>all<\/em> users registered for a particular event. <\/li>\n<li>Scaling out and load balancing events using persistent queues. <\/li>\n<\/ol>\n<p>You can get the update from <a href=\"http:\/\/nuget.org\/\">nuget.org<\/a> by looking for the packages under <a href=\"https:\/\/www.nuget.org\/packages?q=Microsoft.AspNet.WebHooks\">Microsoft.AspNet.WebHooks.*<\/a> with \u2018preview\u2019 filter enabled. If you haven\u2019t heard about ASP.NET WebHooks then you may want to look at <a href=\"http:\/\/blogs.msdn.com\/b\/webdev\/archive\/2015\/09\/04\/introducing-microsoft-asp-net-webhooks-preview.aspx\">Introducing Microsoft ASP.NET WebHooks Preview<\/a>. As this blog is about the sending side, the blog <a href=\"http:\/\/blogs.msdn.com\/b\/webdev\/archive\/2015\/09\/15\/sending-webhooks-with-asp-net-webhooks-preview.aspx\">Sending WebHooks with ASP.NET WebHooks Preview<\/a> also provides good background.<\/p>\n<h3>Sending Events to All Users<\/h3>\n<p>In addition to sending events in the form of WebHooks to individual users, it is now possible to send WebHooks to all users who have registered for a particular event. We now expose the method <strong>NotifyAllAsync<\/strong> in the same places where you could use <strong>NotifyAsync<\/strong> to send notifications to all active registered WebHooks. <\/p>\n<p>Also, you can use a predicate as a filter if you want to control which users get the WebHook notification. For example, in the below illustration, Receiver 2 and 5 don\u2019t get this particular event due to the predicate.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/12\/2480.NotifyAll_thumb_2D1D67CC.png\"><img decoding=\"async\" title=\"NotifyAll\" style=\"border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;float: none;padding-top: 0px;padding-left: 0px;margin: 0px auto;padding-right: 0px;border-top-width: 0px\" border=\"0\" alt=\"NotifyAll\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/12\/2480.NotifyAll_thumb_2D1D67CC.png\" width=\"600\" height=\"344\" \/><\/a><\/p>\n<p>Using <strong>NotifyAllAsync<\/strong> is very similar to using <strong>NotifyAsync<\/strong> and it is available in the same places. Assuming you have a project already set up to support WebHooks as described in the blog entry <a href=\"http:\/\/blogs.msdn.com\/b\/webdev\/archive\/2015\/09\/15\/sending-webhooks-with-asp-net-webhooks-preview.aspx\">Sending WebHooks with ASP.NET WebHooks Preview<\/a> or using <a href=\"https:\/\/github.com\/aspnet\/WebHooks\/tree\/dev\/samples\/CustomSender\">this sample<\/a>, here\u2019s how you can use <strong>NotifyAllAsync<\/strong> in an MVC controller:<\/p>\n<p id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\">[Authorize]<br \/><span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">class<\/span> NotifyController : Controller<br \/>{<br \/>    [HttpPost]<br \/>    <span style=\"color: #0000ff\">public<\/span> async Task&lt;ActionResult&gt; Submit()<br \/>    {<br \/>        <span style=\"color: #008000\">\/\/ Create an event with action 'event1' and additional data<\/span><br \/>        await <span style=\"color: #0000ff\">this<\/span>.NotifyAsync(<span style=\"color: #006080\">&quot;event1&quot;<\/span>, <span style=\"color: #0000ff\">new<\/span> { P1 = <span style=\"color: #006080\">&quot;p1&quot;<\/span> });<br \/> <br \/>        <span style=\"color: #008000\">\/\/ Send event1 to all users not called 'henrik'<\/span><br \/>        <font style=\"background-color: #ffff00\">await <span style=\"color: #0000ff\">this<\/span>.NotifyAllAsync(<span style=\"color: #006080\">&quot;event1&quot;<\/span>, <br \/>            <span style=\"color: #0000ff\">new<\/span> { P1 = <span style=\"color: #006080\">&quot;p1&quot;<\/span> }, (webHook, user) =&gt; { <span style=\"color: #0000ff\">return<\/span> user != <span style=\"color: #006080\">&quot;henrik&quot;<\/span> });<br \/><\/font> <br \/>        <span style=\"color: #0000ff\">return<\/span> <span style=\"color: #0000ff\">new<\/span> EmptyResult();<br \/>    }<br \/>}<\/pre>\n<p>The model looks exactly the same in a Web API controller:<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\">[Authorize]<br \/><span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">class<\/span> NotifyApiController : ApiController<br \/>{<br \/>    <span style=\"color: #0000ff\">public<\/span> async Task&lt;IHttpActionResult&gt; Post()<br \/>    {<br \/>        <span style=\"color: #008000\">\/\/ Create an event with 'event2' and additional data<\/span><br \/>        await <span style=\"color: #0000ff\">this<\/span>.NotifyAsync(<span style=\"color: #006080\">&quot;event2&quot;<\/span>, <span style=\"color: #0000ff\">new<\/span> { P1 = <span style=\"color: #006080\">&quot;p1&quot;<\/span> });<br \/> <br \/>        <span style=\"color: #008000\">\/\/ Send event2 to all users not called 'henrik'<\/span><br \/>        <font style=\"background-color: #ffff00\">await <span style=\"color: #0000ff\">this<\/span>.NotifyAllAsync(<span style=\"color: #006080\">&quot;event2&quot;<\/span>,<br \/>            <span style=\"color: #0000ff\">new<\/span> { P1 = <span style=\"color: #006080\">&quot;p1&quot;<\/span> }, (webHook, user) =&gt; { <span style=\"color: #0000ff\">return<\/span> user != <span style=\"color: #006080\">&quot;henrik&quot;<\/span> });<br \/><\/font> <br \/>        <span style=\"color: #0000ff\">return<\/span> Ok();<br \/>    }<br \/>}<\/pre>\n<p><\/div>\n<h3>Sending WebHooks: Scaling Out and Load Balancing<\/h3>\n<p>We have introduced a new <a href=\"https:\/\/github.com\/aspnet\/WebHooks\/blob\/dev\/src\/Microsoft.AspNet.WebHooks.Custom\/WebHooks\/IWebHookSender.cs\">IWebHookSender<\/a> abstraction which allows you to scale-out and load-balance the act of sending out WebHooks. That is, instead of having the Web server directly sending out WebHooks, it is now possible to have an architecture like this allowing you to both persist WebHooks on the sender side and to scale up and out as you want:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/12\/6888.ScaleOut_thumb_212908D4.png\"><img decoding=\"async\" title=\"ScaleOut\" style=\"border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;float: none;padding-top: 0px;padding-left: 0px;margin: 0px auto;padding-right: 0px;border-top-width: 0px\" border=\"0\" alt=\"ScaleOut\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/12\/6888.ScaleOut_thumb_212908D4.png\" width=\"600\" height=\"247\" \/><\/a><\/p>\n<p>To illustrate this, we provide out of the box support for sending WebHooks via an <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/azure\/dd179353.aspx\">Azure Storage Queue<\/a> but you can hook&#160; in any kind of queue. Once you have a project already set up to support WebHooks (like <a href=\"https:\/\/github.com\/aspnet\/WebHooks\/tree\/dev\/samples\/CustomSender\">this sample<\/a>) as described in the blog entry <a href=\"http:\/\/blogs.msdn.com\/b\/webdev\/archive\/2015\/09\/15\/sending-webhooks-with-asp-net-webhooks-preview.aspx\">Sending WebHooks with ASP.NET WebHooks Preview<\/a>, configuring it to support Azure Storage Queues is quite simple:<\/p>\n<ol>\n<li>On the frontend side, you register <a href=\"https:\/\/github.com\/aspnet\/WebHooks\/blob\/dev\/src\/Microsoft.AspNet.WebHooks.Custom.AzureStorage\/WebHooks\/AzureWebHookSender.cs\">a special IWebHookSender implementation<\/a> which simply submits all WebHooks to an Azure Storage Queue. <\/li>\n<li>On the sender side, you use the <a href=\"https:\/\/github.com\/aspnet\/WebHooks\/blob\/dev\/src\/Microsoft.AspNet.WebHooks.Custom.AzureStorage\/WebHooks\/AzureWebHookDequeueManager.cs\">AzureWebHookDequeueManager class<\/a> which dequeues messages from the Azure Storage Queue and then sends them out as WebHooks. <\/li>\n<\/ol>\n<p>To register the queue <strong>IWebHookSender<\/strong> implementation (part of the <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.AspNet.WebHooks.Custom.AzureStorage\">Microsoft.AspNet.WebHooks.Custom.AzureStorage nuget package<\/a>) on the <em>frontend<\/em>, simply add this line to the initialization:<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\"><span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">static<\/span> <span style=\"color: #0000ff\">class<\/span> WebApiConfig<br \/>{<br \/>    <span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">static<\/span> <span style=\"color: #0000ff\">void<\/span> Register(HttpConfiguration config)<br \/>    {<br \/>        <span style=\"color: #008000\">\/\/ Web API routes<\/span><br \/>        config.MapHttpAttributeRoutes();<br \/> <br \/>        config.Routes.MapHttpRoute(<br \/>            name: <span style=\"color: #006080\">&quot;DefaultApi&quot;<\/span>,<br \/>            routeTemplate: <span style=\"color: #006080\">&quot;api\/{controller}\/{id}&quot;<\/span>,<br \/>            defaults: <span style=\"color: #0000ff\">new<\/span> { id = RouteParameter.Optional }<br \/>        );<br \/> <br \/>        <span style=\"color: #008000\">\/\/ Load basic support for sending WebHooks<\/span><br \/>        config.InitializeCustomWebHooks();<br \/> <br \/>        <span style=\"color: #008000\">\/\/ Load Azure Storage (or SQL) for persisting subscriptions<\/span><br \/>        config.InitializeCustomWebHooksAzureStorage();<br \/> <br \/>        <span style=\"color: #008000\">\/\/ Load Azure Queued Sender for enqueueing outgoing WebHooks to an Azure Storage Queue<\/span><br \/>        <font style=\"background-color: #ffff00\">config.InitializeCustomWebHooksAzureQueueSender();<br \/><\/font> <br \/>        <span style=\"color: #008000\">\/\/ Load Web API controllers for managing subscriptions<\/span><br \/>        config.InitializeCustomWebHooksApis();<br \/>    }<br \/>}<\/pre>\n<p><\/div>\n<p>To configure the frontend, you need to set the Azure Storage connection string, that it should use, for example:<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\"><span style=\"color: #0000ff\">&lt;<\/span><span style=\"color: #800000\">connectionStrings<\/span><span style=\"color: #0000ff\">&gt;<\/span><br \/>  <span style=\"color: #0000ff\">&lt;<\/span><span style=\"color: #800000\">add<\/span> <span style=\"color: #ff0000\">name<\/span><span style=\"color: #0000ff\">=&quot;MS_AzureStoreConnectionString&quot;<\/span> <span style=\"color: #ff0000\">connectionString<\/span><span style=\"color: #0000ff\">=&quot;UseDevelopmentStorage=true;&quot;<\/span> <span style=\"color: #0000ff\">\/&gt;<\/span><br \/><span style=\"color: #0000ff\">&lt;\/<\/span><span style=\"color: #800000\">connectionStrings<\/span><span style=\"color: #0000ff\">&gt;<\/span><\/pre>\n<p><\/div>\n<p>On the <em>sender<\/em> side, you now need a process that can dequeue messages from the Azure Storage Queue and send them out to the targeted WebHook recipients. This can be a simple command line program (like <a href=\"https:\/\/github.com\/aspnet\/WebHooks\/tree\/dev\/samples\/CustomSender.QueuedSender\">this sample<\/a>):<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\"><span style=\"color: #0000ff\">internal<\/span> <span style=\"color: #0000ff\">class<\/span> Program<br \/>{<br \/>    <span style=\"color: #0000ff\">private<\/span> <span style=\"color: #0000ff\">const<\/span> <span style=\"color: #0000ff\">string<\/span> QueueConnectionString = <span style=\"color: #006080\">&quot;MS_AzureStoreConnectionString&quot;<\/span>;<br \/> <br \/>    <span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">static<\/span> <span style=\"color: #0000ff\">void<\/span> Main(<span style=\"color: #0000ff\">string<\/span>[] args)<br \/>    {<br \/>        CancellationTokenSource cancellationTokenSource = <span style=\"color: #0000ff\">new<\/span> CancellationTokenSource();<br \/>        Task.Run(() =&gt; DequeueAndSendWebHooks(cancellationTokenSource.Token));<br \/> <br \/>        Console.WriteLine(<span style=\"color: #006080\">&quot;Hit ENTER to exit!&quot;<\/span>);<br \/>        Console.ReadLine();<br \/> <br \/>        cancellationTokenSource.Cancel();<br \/>    }<br \/> <br \/>    <span style=\"color: #0000ff\">private<\/span> <span style=\"color: #0000ff\">static<\/span> async Task DequeueAndSendWebHooks(CancellationToken cancellationToken)<br \/>    {<br \/>        <span style=\"color: #008000\">\/\/ Create the dequeue manager<\/span><br \/>        <span style=\"color: #0000ff\">string<\/span> connectionString = <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ConfigurationManager.ConnectionStrings[QueueConnectionString].ConnectionString;<br \/>        ILogger logger = CommonServices.GetLogger();<br \/>        <font style=\"background-color: #ffff00\">AzureWebHookDequeueManager manager = <span style=\"color: #0000ff\">new<\/span> AzureWebHookDequeueManager(connectionString, logger);<\/font><br \/> <br \/>        <span style=\"color: #008000\">\/\/ Start the dequeue manager<\/span><br \/>        <font style=\"background-color: #ffff00\">await manager.Start(cancellationToken);<br \/><\/font>    }<br \/>}<\/pre>\n<p><\/div>\n<p>The two highlighted lines are the key ones \u2013 the first creates the <strong>AzureWebHookDequeueManager<\/strong> using the given connection string pointing to the queue. The second line starts an event loop returning a <strong>Task<\/strong> that you can cancel when you are done. The <strong>AzureWebHookDequeueManager<\/strong> will periodically poll for new messages in the queue and send them out as WebHooks.<\/p>\n<p>If the WebHook requests either succeed or return HTTP status code 410 Gone, then we consider the message delivered and delete it from the queue. Otherwise we leave it in the queue ensuring that it will get another chance of being sent out after a couple of minutes. After 3 attempts we give up and delete the message from the queue regardless. For more information about Azure Storage Queues, please see the blog <a href=\"https:\/\/azure.microsoft.com\/en-us\/documentation\/articles\/storage-dotnet-how-to-use-queues\/\">How to use Queue storage from .NET<\/a>.<\/p>\n<p>Like for the sender side, we can provide the Azure Storage connection string in the config file, for example:<\/p>\n<div>\n<pre id=\"codeSnippet\"><span style=\"color: #0000ff\">&lt;<\/span><span style=\"color: #800000\">connectionStrings<\/span><span style=\"color: #0000ff\">&gt;<\/span><br \/>  <span style=\"color: #0000ff\">&lt;<\/span><span style=\"color: #800000\">add<\/span> <span style=\"color: #ff0000\">name<\/span><span style=\"color: #0000ff\">=&quot;MS_AzureStoreConnectionString&quot;<\/span> <span style=\"color: #ff0000\">connectionString<\/span><span style=\"color: #0000ff\">=&quot;UseDevelopmentStorage=true;&quot;<\/span> <span style=\"color: #0000ff\">\/&gt;<\/span><br \/><span style=\"color: #0000ff\">&lt;\/<\/span><span style=\"color: #800000\">connectionStrings<\/span><span style=\"color: #0000ff\">&gt;<\/span><br \/><\/pre>\n<\/div>\n<div>&#160;<\/div>\n<div>That\u2019s it \u2013 I hope you enjoy these new features in ASP.NET WebHooks!<\/div>\n<p>Happy New Year!<\/p>\n<p>Henrik<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We just released a new update of ASP.NET WebHooks with a couple of interesting new features on the WebHooks sender side \u2013 that is, when you want to send WebHooks to others when some event happens \u2013 including: Sending events to all users registered for a particular event. Scaling out and load balancing events using [&hellip;]<\/p>\n","protected":false},"author":403,"featured_media":58792,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[197],"tags":[34,7505,7506],"class_list":["post-6082","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aspnet","tag-asp-net-web-api","tag-asp-net-webhooks","tag-webhooks"],"acf":[],"blog_post_summary":"<p>We just released a new update of ASP.NET WebHooks with a couple of interesting new features on the WebHooks sender side \u2013 that is, when you want to send WebHooks to others when some event happens \u2013 including: Sending events to all users registered for a particular event. Scaling out and load balancing events using [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/6082","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/403"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=6082"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/6082\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=6082"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=6082"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=6082"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}