{"id":5881,"date":"2015-09-15T07:33:10","date_gmt":"2015-09-15T07:33:10","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/webdev\/2015\/09\/15\/sending-webhooks-with-asp-net-webhooks-preview\/"},"modified":"2023-11-09T13:15:49","modified_gmt":"2023-11-09T21:15:49","slug":"sending-webhooks-with-asp-net-webhooks-preview","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/sending-webhooks-with-asp-net-webhooks-preview\/","title":{"rendered":"Sending WebHooks with ASP.NET WebHooks Preview"},"content":{"rendered":"<p>In the blog <a href=\"http:\/\/blogs.msdn.com\/b\/webdev\/archive\/2015\/09\/04\/introducing-microsoft-asp-net-webhooks-preview\">Introducing Microsoft ASP.NET WebHooks Preview<\/a>, we gave an overview of how to work with Microsoft ASP.NET WebHooks. We mentioned that it is not only possible to <em>receive<\/em> WebHooks from others but also to add support for <em>sending<\/em> WebHooks from your Web Application. This blog goes into detail on how to do that.<\/p>\n<p>Sending WebHooks is slightly more involved in that there are more things to keep track of. In order to support other APIs registering for WebHooks from your ASP.NET application, we need to provide support for:<\/p>\n<ul>\n<li>Exposing which events subscribers can subscribe to, for example <em>Item Created<\/em> and <em>Item Deleted<\/em>;<\/li>\n<li>Managing subscribers and their registered WebHooks which includes persisting them so that they don\u2019t disappear;<\/li>\n<li>Handling per-user events in the system and determine which WebHooks should get fired so that WebHooks go to the correct receivers. For example, if user <em>A<\/em> caused an <em>Item Created<\/em> event to fire then determine which WebHooks registered by user <em>A<\/em> should be sent. We don\u2019t want events for user <em>A<\/em> to be sent to user <em>B<\/em>.<\/li>\n<li>Sending WebHooks to receivers with matching WebHook registrations.<\/li>\n<\/ul>\n<p>Microsoft ASP.NET WebHooks help you throughout this process making it simpler to support WebHooks on your own:<\/p>\n<ul>\n<li>It provides support for managing which events users can subscribe to and allowing these to be exposed to clients;<\/li>\n<li>It provides a mechanism for managing registered WebHooks. This can be done using an API controller for registering\/unregistering WebHooks using REST or an MVC controller for a UI oriented approach. In addition, it provides a pluggable model for persisting WebHook registrations and out-of-box support for storing WebHook registrations in Azure Table Storage;<\/li>\n<li>It provides a mechanism for finding matching WebHook registrations and for determining which WebHooks to send as a result;<\/li>\n<li>It supports sending WebHooks handling errors and retrying requests for a number of times before giving up.<\/li>\n<\/ul>\n<p><strong>Note:<\/strong> We are adding topics to the <a href=\"http:\/\/docs.asp.net\/projects\/aspnetwebhooks\/en\/latest\/\">Microsoft ASP.NET WebHooks online documentation<\/a> so please also check there for additional details as they become available.<\/p>\n<h3>Creating a Sample Web Application<\/h3>\n<p>First we need an <strong>ASP.NET Web Application<\/strong> which has some form of authentication enabled so that we know who the client user is. The reason for this is that we want to only have WebHooks go to the users who registered for them. In this example we create up an <strong>MVC + Web API<\/strong> project using <strong>Individual User Accounts<\/strong> as follows:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/16\/2018\/10\/8713.WebHookSenderProject_1BD718C0.png\"><img decoding=\"async\" style=\"float: none;padding-top: 0px;padding-left: 0px;margin-left: auto;padding-right: 0px;margin-right: auto;border-width: 0px\" title=\"WebHookSenderProject\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/09\/6177.WebHookSenderProject_thumb_5481C2CD.png\" alt=\"WebHookSenderProject\" width=\"500\" height=\"390\" border=\"0\" \/><\/a><\/p>\n<p>Btw, you can also <a href=\"https:\/\/github.com\/aspnet\/WebHooks\/tree\/master\/samples\/\">look at the sample doing exactly this<\/a>.<\/p>\n<h3>Adding the Nugets<\/h3>\n<p>The support for sending WebHooks is provided by the following Nuget packages:<\/p>\n<ul>\n<li><b><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.AspNet.WebHooks.Custom\">Microsoft.AspNet.WebHooks.Custom<\/a><\/b>: This package provides the core functionality for adding WebHook support to your ASP.NET project. The functionality enables users to register WebHooks using a simple pub\/sub model and for your code to send WebHooks to receivers with matching WebHook registrations.<\/li>\n<li><b><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.AspNet.WebHooks.Custom.AzureStorage\">Microsoft.AspNet.WebHooks.Custom.AzureStorage<\/a> <\/b>This package provides optional support for persisting WebHook registrations in Microsoft Azure Table Storage.<\/li>\n<li><b><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.AspNet.WebHooks.Custom.SqlStorage\">Microsoft.AspNet.WebHooks.Custom.SqlStorage<\/a> <\/b>This package provides optional support for persisting WebHook registrations in SQL. See <a href=\"http:\/\/blogs.msdn.com\/b\/webdev\/archive\/2015\/11\/07\/updates-to-microsoft-asp-net-webhooks-preview.aspx\">Updates to Microsoft ASP.NET WebHooks Preview<\/a> for a description of how to use it.<\/li>\n<li><b><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.AspNet.WebHooks.Custom.Mvc\">Microsoft.AspNet.WebHooks.Custom.Mvc<\/a> <\/b>This package exposes optional helpers for accessing WebHooks functionality from within ASP.NET MVC Controllers. The helpers assist in providing WebHook registration through MVC controllers as well as creating event notification to be sent to WebHook registrants.<\/li>\n<li><b><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.AspNet.WebHooks.Custom.Api\">Microsoft.AspNet.WebHooks.Custom.Api<\/a> <\/b>This package contains an\u00a0 optional set of ASP.NET Web API Controllers for managing filters and registrations through a REST-style interface.<\/li>\n<\/ul>\n<p>In this example we will use all four packages for providing a basic WebHooks implementation which stores WebHook registrations in Azure Table Storage and exposes a REST API for subscribing to WebHooks. However, ASP.NET WebHooks is built around Dependency Injection and so most parts of the system are pluggable allowing you to provide alternative implementations as needed.<\/p>\n<p>After adding the four packages to your project, initialize them by adding the following <span style=\"background-color: #ffff00\">three lines<\/span> to <strong>WebApiConfig.Register<\/strong>:<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre class=\"lang:default decode:true \">public static class WebApiConfig\r\n{\r\n    public static void Register(HttpConfiguration config)\r\n    {\r\n        \/\/ Web API configuration and services\r\n        \/\/ Web API routes\r\n        config.MapHttpAttributeRoutes();\r\n\r\n        config.Routes.MapHttpRoute(\r\n        name: \"DefaultApi\",\r\n        routeTemplate: \"api\/{controller}\/{id}\",\r\n        defaults: new \r\n        { \r\n            id = RouteParameter.Optional \r\n        }\r\n        );\r\n\r\n        \/\/ Load Web API controllers and Azure Storage store\r\n        config.InitializeCustomWebHooks();\r\n        config.InitializeCustomWebHooksAzureStorage();\r\n        config.InitializeCustomWebHooksApis();\r\n    }\r\n}<\/pre>\n<pre class=\"lang:default decode:true\">\/\/ Web API routes\r\nconfig.MapHttpAttributeRoutes();\r\n\r\nconfig.Routes.MapHttpRoute(\r\nname: \"DefaultApi\",\r\nrouteTemplate: \"api\/{controller}\/{id}\",\r\ndefaults: new { id = RouteParameter.Optional }\r\n);\r\n\r\n\/\/ Load Web API controllers and Azure Storage store\r\nconfig.InitializeCustomWebHooks();\r\nconfig.InitializeCustomWebHooksAzureStorage();\r\nconfig.InitializeCustomWebHooksApis();\r\n}\r\n}<\/pre>\n<p><span style=\"color: inherit;font-family: inherit;font-size: 1.75rem\">Defining Event Filters<\/span><\/p>\n<\/div>\n<p>Now let\u2019s look at defining the set of event filters that consumers of your WebHooks can use when subscribing for event notifications. The filters indicate which events the consumer is interested in. By default, the system registers a <em>wildcard filter<\/em> which allows users to register for all events. Event filters are registered by providing one or more implementations of the <strong>IWebHookFilterProvider<\/strong> interface which simply exposes <strong>GetFiltersAsync<\/strong>. Add a class called<strong> MyFilterProvider<\/strong> like below defining two events (<em>event1<\/em> and <em>event2<\/em>):<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre class=\"lang:default decode:true \">\/\/\/ &lt;summary&gt;\r\n\/\/\/ Use a IWebHookFilterProvider implementation to describe the events that users can \r\n\/\/\/ subscribe to. A wildcard is always registered meaning that users can register for \r\n\/\/\/ \"all events\". It is possible to have 0, 1, or more IWebHookFilterProvider \r\n\/\/\/ implementations.\r\n\/\/\/ &lt;\/summary&gt;\r\npublic class MyFilterProvider : IWebHookFilterProvider\r\n{\r\n    private readonly Collection&lt;WebHookFilter&gt; filters = new Collection&lt;WebHookFilter&gt;\r\n    {\r\n        new WebHookFilter { Name = \"event1\", Description = \"This event happened.\"},\r\n        new WebHookFilter { Name = \"event2\", Description = \"This event happened.\"},\r\n    };\r\n\r\n    public Task&lt;Collection&lt;WebHookFilter&gt;&gt; GetFiltersAsync() \r\n    { \r\n        return Task.FromResult(this.filters); \r\n    } \r\n}\r\n<\/pre>\n<\/div>\n<p>When ASP.NET WebHooks starts up, it looks for all <strong>IWebHookFilterProvider<\/strong> implementations and gathers the combined set of event filters. The filters are made available so that they can be incorporated into an MVC UI or exposed through the REST-style API for managing registrations.<\/p>\n<h3>Managing WebHook Subscriptions<\/h3>\n<p>WebHook subscriptions are managed through an interface called <strong>IWebHookStore<\/strong> which provide an abstraction for querying, inserting, updating, and deleting subscriptions. The default <strong>IWebHookStore<\/strong> implementation is in-memory-only, but for more realistic scenarios, you can use the <a href=\"https:\/\/github.com\/aspnet\/\">Azure Table Storage implementation<\/a>, or write your own. Just like the store can be backed by any number of implementations, the store can be accessed in a number of ways:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/16\/2018\/10\/1616.SubscriptionStore_3B23779E.png\"><img decoding=\"async\" style=\"float: none;padding-top: 0px;padding-left: 0px;margin-left: auto;padding-right: 0px;margin-right: auto;border-width: 0px\" title=\"SubscriptionStore\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/09\/5428.SubscriptionStore_thumb_4D000B6B.png\" alt=\"SubscriptionStore\" width=\"500\" height=\"221\" border=\"0\" \/><\/a><\/p>\n<p>That is, you can for example write an MVC controller which exposes subscription management, or you can use the provided Web API implementation which exposes it using a REST-style interface. In this example we will go with the Web API and store the data in Azure Table Storage, the but you can provide the exact experience you want.<\/p>\n<p>We just use the local Development Storage so set the <strong>MS_AzureStoreConnectionString<\/strong> connection string as follows:<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre class=\"lang:default decode:true\">&lt;connectionStrings&gt;\r\n  &lt;add name=\"MS_AzureStoreConnectionString\" connectionString=\"UseDevelopmentStorage=true;\"\/&gt;\r\n&lt;\/connectionStrings&gt;<\/pre>\n<p><span style=\"color: inherit;font-family: inherit;font-size: 1.75rem\">Sending Notifications<\/span><\/p>\n<\/div>\n<p>Let\u2019s generate an event! Events are matched against the registered WebHooks and if matches are found then an event notification is fired in the form of a WebHook. Generating WebHook notifications typically happens from within an MVC controller or a Web API controller but can actually happen from anywhere. In the following we will focus on sending them from an MVC controller and a Web API controller.<\/p>\n<p>Add an empty MVC Controller called <strong>NotifyController<\/strong> with a <strong>Submit<\/strong> action that generates an event using the <strong>NotifyAsync<\/strong> method:<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre class=\"lang:default decode:true\">[Authorize]\r\npublic class NotifyController : Controller\r\n{\r\n    [HttpPost]\r\n    public async Task&lt;ActionResult&gt; Submit()\r\n    {\r\n        \/\/ Create an event with action 'event1' and additional data\r\n        await this.NotifyAsync(\"event1\", new { P1 = \"p1\" });\r\n\r\n        return new EmptyResult(); \r\n    } \r\n}<\/pre>\n<p><span style=\"font-size: 1rem\">Note that we only want WebHooks to be sent to the current user. That is, we don\u2019t want user <\/span><em style=\"font-size: 1rem\">A<\/em><span style=\"font-size: 1rem\">\u2019s events going to user <\/span><em style=\"font-size: 1rem\">B<\/em><span style=\"font-size: 1rem\"> and vice versa. This means that to generate an event we must have a valid user ID. In the case of the controllers we do this by ensuring that the user is authenticated using the <\/span><strong style=\"font-size: 1rem\">[Authorize]<\/strong><span style=\"font-size: 1rem\"> attribute.<\/span><\/p>\n<\/div>\n<p>The input data allows the submitter to include additional data in the WebHook which is then sent to matching receivers. This can be anything that the notification needs to convey data about the event.<\/p>\n<p>Similarly, add an empty Web API Controller called <strong>NotifyApiController<\/strong> with a Post action that generates an event using the <strong>NotifyAsync<\/strong> method:<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre class=\"lang:default decode:true\">[Authorize]\r\npublic class NotifyApiController : ApiController\r\n{\r\n    public async Task Post()\r\n    {\r\n        \/\/ Create an event with 'event2' and additional data\r\n        await this.NotifyAsync(\"event2\", new { P1 = \"p1\" });\r\n    }\r\n}<\/pre>\n<\/div>\n<p>The use of actions and data is identical to that of MVC controllers and again, we require authentication to get a valid user.<\/p>\n<h3>Trying it Out<\/h3>\n<p>That\u2019s all the configuration we need. However, in order to try it out we need to set up a test client that can subscribe and receive WebHooks. For this scenario we just add the test client straight to the current Web Application project. In a more realistic scenario, it would of course be a separate project, but this suffices to show the point. The flow we are going for is as follows:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/09\/5047.WebHookFlow_thumb_214F416F.png\"><img decoding=\"async\" style=\"float: none;padding-top: 0px;padding-left: 0px;margin-left: auto;padding-right: 0px;margin-right: auto;border-width: 0px\" title=\"WebHookFlow\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/09\/5047.WebHookFlow_thumb_214F416F.png\" alt=\"WebHookFlow\" width=\"400\" height=\"342\" border=\"0\" \/><\/a><\/p>\n<p>To set up the test client, add the <b><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.AspNet.WebHooks.Receivers.Custom\/1.2.0-beta\">Microsoft.AspNet.WebHooks.Receivers.Custom<\/a><\/b> to your Web Application project and <span style=\"background-color: #ffff00\">add this<\/span> to your <strong>WebApiConfig.Register<\/strong> method:<\/p>\n<div>\n<pre class=\"lang:default decode:true \">public static void Register(HttpConfiguration config)\r\n{\r\n\/\/ Web API configuration and services\r\n\r\n\/\/ Web API routes\r\nconfig.MapHttpAttributeRoutes();\r\n\r\nconfig.Routes.MapHttpRoute(\r\nname: \"DefaultApi\",\r\nrouteTemplate: \"api\/{controller}\/{id}\",\r\ndefaults: new { id = RouteParameter.Optional }\r\n);\r\n\r\n\/\/ Load Web API controllers and Azure Storage store\r\nconfig.InitializeCustomWebHooks();\r\nconfig.InitializeCustomWebHooksAzureStorage();\r\nconfig.InitializeCustomWebHooksApis();\r\nconfig.InitializeReceiveCustomWebHooks();\r\n}<\/pre>\n<p><span style=\"font-size: 1rem\">Configure the receiver with the following Application Setting added to <\/span><strong style=\"font-size: 1rem\">Web.Config<\/strong><span style=\"font-size: 1rem\">:<\/span><\/p>\n<\/div>\n<div><\/div>\n<div>\n<pre class=\"lang:default decode:true\">&lt;appSettings&gt;\r\n  &lt;add key=\"MS_WebHookReceiverSecret_Custom\" value=\"12345678901234567890123456789012\" \/&gt;\r\n&lt;\/appSettings&gt;<\/pre>\n<\/div>\n<div>Next, we add a <strong>TestHandler<\/strong> class to process incoming WebHooks like this:<\/div>\n<div><\/div>\n<div id=\"codeSnippetWrapper\">\n<pre class=\"lang:default decode:true\">public class TestHandler : WebHookHandler\r\n{\r\n    public override Task ExecuteAsync(string receiver, WebHookHandlerContext context)\r\n    {\r\n        return Task.FromResult(true);\r\n    }\r\n}<\/pre>\n<\/div>\n<div>To simulate subscribing and triggering events, we mock up a simple UI in the well-known ASP.NET home page. Open the file <strong>Views\/Home\/Index.cshtml <\/strong>and add the following content right under the <strong>jumbotron <\/strong>div element. In this scenario we subscribe to all events by not including any filters in the subscribe request.<\/div>\n<div><\/div>\n<div><strong>Note:<\/strong> Make sure the <em>WebHookURI<\/em> property points to your actual server by setting the HTTP port to the correct value, for example <a href=\"http:\/\/localhost:59927\/api\/webhooks\/incoming\/custom\">http:\/\/localhost:<strong><span style=\"background-color: #ffff00\">59927<\/span><\/strong>\/api\/webhooks\/incoming\/custom<\/a>:<\/div>\n<div>\n<div><\/div>\n<div id=\"codeSnippetWrapper\">\n<pre class=\"lang:default decode:true\">&lt;form onsubmit=\"return subscribe()\"&gt;\r\n    Subscribe to all events &lt;input type=\"submit\" value=\"submit\"&gt;\r\n&lt;\/form&gt;\r\n&lt;form onsubmit=\"return unsubscribe()\"&gt;\r\n    Unsubscribe from all events &lt;input type=\"submit\" value=\"submit\"&gt;\r\n&lt;\/form&gt;\r\n&lt;form onsubmit=\"return notifymvc()\"&gt;\r\n    Trigger notification through MVC controller &lt;input type=\"submit\" value=\"submit\"&gt;\r\n&lt;\/form&gt;\r\n&lt;form onsubmit=\"return notifyapi()\"&gt;\r\n    Trigger notification through Web API controller &lt;input type=\"submit\" value=\"submit\"&gt;\r\n&lt;\/form&gt;<\/pre>\n<p>function subscribe() {\n$.ajax({\ntype: &#8220;POST&#8221;,\nurl: &#8220;\/api\/webhooks\/registrations&#8221;,\ndata: JSON.stringify({\nWebHookUri: &#8220;http:\/\/localhost:59927\/api\/webhooks\/incoming\/custom&#8221;,\nSecret: &#8220;12345678901234567890123456789012&#8221;,\nDescription: &#8220;My first WebHook!&#8221;\n}),\ncontentType: &#8220;application\/json; charset=utf-8&#8221;,\ndataType: &#8220;json&#8221;,\nsuccess: function(data, status) { alert(status); },\nfailure: function(errMsg) { alert(errMsg); }\n});\nreturn false;\n}<\/p>\n<p>function unsubscribe() {\n$.ajax({\nurl: &#8220;\/api\/webhooks\/registrations&#8221;,\ntype: &#8216;DELETE&#8217;,\nsuccess: function (data, status) { alert(status); },\nfailure: function(errMsg) { alert(errMsg); }\n});\nreturn false;\n}<\/p>\n<p>function notifymvc() {\n$.post(&#8220;\/notify\/submit&#8221;,\n{ },\nfunction (data, status) { alert(&#8220;Data: &#8221; + data + &#8220;nStatus: &#8221; + status); });\nreturn false;\n}<\/p>\n<p>function notifyapi() {\n$.post(&#8220;\/api\/notifyapi&#8221;,\n{ },\nfunction (data, status) { alert(&#8220;Data: &#8221; + data + &#8220;nStatus: &#8221; + status); });\nreturn false;\n}<\/p>\n<p>That should give you four very fancy buttons on the home page looking like this:<\/p>\n<\/div>\n<\/div>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/16\/2018\/10\/6102.SenderTestPage_6AE1201D.png\"><img decoding=\"async\" style=\"float: none;padding-top: 0px;padding-left: 0px;margin-left: auto;padding-right: 0px;margin-right: auto;border-width: 0px\" title=\"SenderTestPage\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/09\/5875.SenderTestPage_thumb_0370BD6E.png\" alt=\"SenderTestPage\" width=\"600\" height=\"418\" border=\"0\" \/><\/a><\/p>\n<p>First you have to authenticate by logging in using the <strong>login flow<\/strong> in the top right corner. Just create a new user\/password \u2013 the details don\u2019t matter:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/16\/2018\/10\/7127.SenderUser_1F154964.png\"><img decoding=\"async\" style=\"float: none;padding-top: 0px;padding-left: 0px;margin-left: auto;padding-right: 0px;margin-right: auto;border-width: 0px\" title=\"SenderUser\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/09\/8611.SenderUser_thumb_73647F67.png\" alt=\"SenderUser\" width=\"600\" height=\"418\" border=\"0\" \/><\/a><\/p>\n<p>When you hit <strong>Subscribe<\/strong>, an entry should be stored in Azure Table Storage. You can inspect that this happened by looking at the store in Visual Studio (note that the content is encrypted with a roll-over key so that none of the client secrets are visible).<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/09\/6406.Subscription_thumb_68F560BF.png\"><img decoding=\"async\" style=\"float: none;padding-top: 0px;padding-left: 0px;margin-left: auto;padding-right: 0px;margin-right: auto;border-width: 0px\" title=\"Subscription\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/09\/6406.Subscription_thumb_68F560BF.png\" alt=\"Subscription\" width=\"600\" height=\"218\" border=\"0\" \/><\/a><\/p>\n<p>Now hit either of the Trigger buttons on the home page which will fire off events. As a result, you should be able to see this in your handler like this:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/09\/6012.HandlerDebug_thumb_16768083.png\"><img decoding=\"async\" style=\"float: none;padding-top: 0px;padding-left: 0px;margin-left: auto;padding-right: 0px;margin-right: auto;border-width: 0px\" title=\"HandlerDebug\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2015\/09\/6012.HandlerDebug_thumb_16768083.png\" alt=\"HandlerDebug\" width=\"600\" height=\"168\" border=\"0\" \/><\/a><\/p>\n<p>That\u2019s it \u2013 you now have full support for sending WebHooks!<\/p>\n<p>Have fun!<\/p>\n<p>Henrik<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the blog Introducing Microsoft ASP.NET WebHooks Preview, we gave an overview of how to work with Microsoft ASP.NET WebHooks. We mentioned that it is not only possible to receive WebHooks from others but also to add support for sending WebHooks from your Web Application. This blog goes into detail on how to do that. [&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":[7333,34,7505,7506],"class_list":["post-5881","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aspnet","tag-asp-net-mvc","tag-asp-net-web-api","tag-asp-net-webhooks","tag-webhooks"],"acf":[],"blog_post_summary":"<p>In the blog Introducing Microsoft ASP.NET WebHooks Preview, we gave an overview of how to work with Microsoft ASP.NET WebHooks. We mentioned that it is not only possible to receive WebHooks from others but also to add support for sending WebHooks from your Web Application. This blog goes into detail on how to do that. [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/5881","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=5881"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/5881\/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=5881"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=5881"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=5881"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}