Share via


ASP.NET and Its Love For WebHooks

By now you’ve probably heard that ASP.NET now supports WebHooks and not only does it support them, but it goes along with them quite well.

Disclaimer: If you’ve read my posts before, you probably know by now that I’m not a trumpet-kind of guy to promote things which were already decently promoted by team members, company blogs and other community leaders. More specifically, the announcement for WebHooks support was already made by Scott Guthrie, Scott Hanselman, Henrik F Nielsen and others. If you missed any of these, please go ahead and check them out first.

The announcement regarding ASP.NET WebHooks support has been well covered for the last month or so. So rather than go through the announcement, I wanted to detail the process of sending WebHooks. Before you read on any more, please make sure you read this blog post from Henrik F Nielsen on ‘Sending WebHooks with ASP.NET’ – the article is very thorough and well written, but lacks explaining a few things if you’re new to WebHooks.

Basics

If you’re familiar with WebHooks, skip to Receiving WebHooks. Otherwise, happy reading.

The whole concept of WebHooks isn’t that new anyway since it’s only a further standardization wanna-be of autonomous requests going back and forth between autonomous web servers, by calling specific REST endpoints. When I say standardization wanna-be, I mean that the request which gets sent out the target endpoint will have a specific request body format – within a JSON object in a specific format, as defined by the convention of independent groups working to define the guidelines which will eventually evolve into standards. So the sender is going to specify a few things, such as the reason for why it ended up sending the request in the first place (this is called an event). In other words, WebHooks is nothing else than a convention on what the request body of an automated HTTP request should look like in order for it to get along with other external web services. This link from pbworks.com explains it in more detail.

Taking a closer look at the various services which claim support for WebHooks, such as SendGrid, PayPal, MailChimp, GitHub, Salesforce etc., you come to understand that whenever you, as a user, configure a particular service’s WebHook implementation, you get to a part where you usually put in a URL and possibly select a list of given events which might force that URL to be hit by a new request. If you’ll go over more services’ configuration webpages for WebHooks, you’ll realize that this configuration part is fairly common to all and thus becomes a pattern.

Receiving WebHooks

Until recently, the difficult part was developing your service in such a manner that it understands WebHooks. That was simply so because developing the next GitHub or PayPal service overnight, so that user eventually use it to get WebHooks generated requests for their own web services was… well, let’s face it – unrealistic. Therefore, most articles on-line cover the part of receiving WebHooks and never forget to praise the ASP.NET teams in Redmond for the terrific work they did – they totally deserve it.

Sending WebHooks

However, what if you DO develop the next PayPal? Or maybe simply a number of independent services you want to work and sporadically communicate with each other, in an event-based manner?

Well, on one hand, considering that you want WebHooks to be sent out, you have to remember that WebHooks is, in the end, a fancy name for an HTTP request which contains a special body request format. Therefore, I’d a no-brainer that you could instantiate an object of type HttpClient or WebClient and have the request issued accordingly. But still, remember that if your services are going to be used by external customers, they’ll eventually desire these requests to go to their own services as well. In other words, your services should be able to issue request on an event-based manner to a multitude of HTTP endpoints, based on a series of configurations: which actions, trigger which events and run requests at which URLs.

More specifically, consider that you develop the next most popular online invoicing SaaS API. Since you’re following the best practices for web services, you’ll most likely not generate the invoice and send it to an e-mail address in the web server code-behind code, would you? Instead, you probably architect some sort of n-tier application type where your front-facing web application get any invoice generation requests, respond back with a ‘promise’ that the invoice will be generated and push the request to a queue of some type so that a worker role which actually generates the invoices will work in a nicely load-balanced environment.

The question is now, how could the external clients get notified that a new invoice has been generated and possibly even sent through an e-mail at the e-mail address specified in the request? Well, WebHooks could solve this problem quite nicely:

  1. the worker role would first generate the invoice
  2. once it is generated, considering this is an event of its own type (e.g. invoice_generated), it would raise this event and call a URL the customer has configured, only if the customer chose to receive requests for this event type
  3. next, the worker role would try to send the invoice attached in an e-mail specified by the client when it originally created the request
  4. if the e-mail was sent successfully, the client could again be pinged at the URL the customer configured with another type of event (e.g. email_sent), considering that the customer chose to receive a request for this event type

It’s probably obvious by now that there’s a tremendous amount of work left to be done by the developer in order to send out a WebHook request if that WebHook request is generated by a HttpClient object – or anything similar.

Don’t get me wrong – there’s nothing wrong with this approach. But there’s a better way of doing all this if-registered-get-URL kind of logic when it comes to WebHooks and .NET code.

Put The NuGet Packages To Work

At the time of this writing, there are exactly four NuGet packages containing Microsoft.AspNet.WebHooks.Custom name prefix and the reason for this large number is going to be explained throughout the remainder of this post.

First, there’s a package called simply Microsoft.AspNet.WebHooks.Custom, which is the core package you want to install when you’re creating your own custom WebHook. Additionally, there’s a so-called Microsoft.AspNet.WebHooks.Custom.AzureStorage package which will work like a charm when you want to store your WebHook registrations in a persistent storage – and yes, by now I’ve spoiled the surprise. The NuGet packages not only send WebHooks, but they also do the entire registration and selection based on event registration story for you as well, and this is not exactly obvious in my humble opinion. Third, there’s an Microsoft.AspNet.WebHooks.Custom.Mvc package which aids in the actual registration process, should your application run as an ASP.NET MVC application. Lastly, there’s an Microsoft.AspNet.WebHooks.Custom.API package which handles does a great job by adding an optional set of ASP.NET WebAPI controllers useful for management purposes of WebHooks, in the form of a REST-like API.

I’ll keep things simple in this post, so rather than focus on the magic which comes along with the .Mvc, .AzureStorage and .Api packages, I’ll simply create a console application that will act as a self-registree and sender of WebHooks. In order to intercept the WebHooks and check that the implementation actually works, I’ll create a plain simple WebAPI application and add the required NuGet packages to it so that it can handle incoming WebHooks requests.

The entire source code is available on GitHub here.

As you’ll see, the majority of the code currently runs in Program.cs. The work is done id the Main method is simply in the relation of getting things ready; more specifically, I first instantiate the object called _whStore and _whManager – the latter requires the _whStore as a parameter. These objects are responsible for the following:

    1. What events he/she is interested in, in the form of Filters. This will instruct the manager object which will do the actual sending to only send web hook requests when those specific events occur
    2. It's secret, which should ideally be unique – this secret is to be used in order to calculate a SHA256 hash of the body. The subscriber afterward should only accept WebHooks which contain a properly calculated hash over their request bodies – otherwise, these might be scam WebHooks
    3. A list of HTTP header attributes
    4. List properties which are to be sent out at each and every web hook request with the exact same values, no matter what the event is
  • _whManager is the do-er, the object which will actually send the web hook request. Since it has to know to whom to send the web hook requests in the first place, it requires the WebHookStore-type object sent as a parameter in its constructor. In addition, it also requires an ILogger-type object as a second constructor parameter, which is going to be used as a diagnostics logger

class Program
{
   private static IWebHookManager _whManager;
   private static IWebHookStore _whStore;

   static void Main(string[] args)
   {
       _whStore = new MemoryWebHookStore();
       _whManager = new WebHookManager(_whStore, new TraceLogger());
      SubscribeNewUser();
      SendWebhookAsync().Wait();
      Console.ReadLine();
   }
   private static void SubscribeNewUser()
   {
       var webhook = new WebHook();
       webhook.Filters.Add("event1");
       webhook.Properties.Add("StaticParamA", 10);
       webhook.Properties.Add("StaticParamB", 20);
       webhook.Secret = "PSBuMnbzZqVir4OnN4DE10IqB7HXfQ9l";
       webhook.WebHookUri = "http://www.alexmang.com";
       _whStore.InsertWebHookAsync("user1", webhook);
   }
   private static async Task SendWebhookAsync()
   {
      var notifications = new List<NotificationDictionary> { new NotificationDictionary("event1", new { DynamicParamA = 100, DynamicParamB = 200 }) };
      var x = await _whManager.NotifyAsync("user1", notifications);
   } 
}

 

The good thing about a simple ‘Hello, World’ sample application

The good thing in this sample is that WebHooks can be, in my opinion, self-taught if the proper explanations are added. More specifically, the reason for the existence of the IWebHookStore interface is due to the fact that you’ll most likely NOT use a MemoryWebHookStore in production workloads, simply because stopping the application and running it again will completely delete any subscriber registrations – ouch.

Therefore, implementing the IWebHookManager interface will help you a lot, meaning that you could implement your own database design for storing the subscriber registrations along with all the properties, extra HTTP headers they require whenever, based on the events (a.k.a. actions, a.k.a. filters) they chose in a form somehow. However, please be aware that there’s an.AzureStorage NuGet Package I mentioned earlier which eases the development even further, by auto-“magically” doing the persistent storage part of the registration on your behalf – uber-cool! I’ll detail the process of using Azure Storage as your backend for web hook subscriptions in a future post.

Additionally, there’s an interface for the manager as well which only does two things (currently!) – verify the web hooks registered and create a new notification. There are a few things which are important for you to keep in mind here:

  1. Notification will be done by sending in the user name as a parameter. If it isn’t obvious why you’d do that since you’ve already specified the users’ usernames upon registration, remember the flow: users register, an event occurs in the system on a per-user-action-basis, that particular user gets notified. The second parameter is an enumerable of notification dictionaries, which is actually a list of objects where you specify the event which just occurred and determines the WebHook request to be fired in the first place – since the notification can also send extra data to the subscriber in the request body, this parameter cannot be a simple string, and as such requires two parameters when instantiated: the event name (as a string) and an object which will eventually get serialized as a JSON object.
  2. I’d argue that the default implementation of IWebHookManager, namely WebHookManager, will meet most of your needs and there’s probably little to no reason to implement your own WebHookManager instead. If you’re not convinced, take a look at its source-code (yes, Microsoft DOES LOVE OPEN-SOURCE!) and check out the tremendous work they did so far on the WebHookManager class. I do have to admit though, that in term of coding-style, I’m very unhappy with the fact that if the manager fails to send the web hook request, no exception or error code will ever be thrown from the .NotifyAsync() method – this decision might have been taken since the method will most likely be called from a worker-role-type application which shouldn’t ever freeze due to an unhandled exception. If that is the case, too bad that you, as a developer, cannot take the decision on your own instead. On the other hand though, remember the ILogger object (of type TraceLogger) you used when you originally instantiated the manager – many methods will eventually use the logger to send out diagnostics and these could help a lot when you’re trying to figure out if any web hook requests where sent out.

And since I’ve mentioned ILogger, let me remind you that if you add a trace listener to your application and use the already available TraceLogger type from the NuGet package, you will get the diagnostics data within the trace you’ve added as a trace listener. Should that be of type TextWriterTraceListener, the traces the WebHookManager creates will be written down on the disk.

 <system.diagnostics>
   <trace autoflush="true" indentsize="4">
     <listeners>
      <add name="TextListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="trace.log" />
      <remove name="Default" />
     </listeners>
   </trace>
 </system.diagnostics>

Options, Options, Options…

I’ve mentioned earlier the usefulness of the interfaces the nugget NuGet packages bring along due to their flexibility of covering any scenario you’d need. There’s however something even better than that, and that’s Dependency Injection support. More specifically, the NuGet packages also have a so-called CustomService static class which you can use to create instances of your WebHookManager, WebHookStore and so on and so forth.

Conclusion

WebHooks are here to connect the disconnected nature of the web and are here to stay. They are certainly not a new technology, not even a new concept – but it could still revolutionize the way we trigger our REST-based endpoints to execute task-based operations. If you’re new to WebHooks, get started today. If you’re a hard-core ASP.NET MVC developer, integrate WebHooks in your projects today. And if you’re an Azure Web App developer, why not develop WebJobs triggered by WebHooks? Oops, I spoiled my next post’s surprise http://i1.wp.com/alexmang.com/wp-includes/images/smilies/simple-smile.png?w=580