다음을 통해 공유


ASP.NET: Understanding WebHooks

Download source code:

Bitbucket sample application

Introduction

This is a second article in the series of articles on Microsoft ASP.NET WebHooks.

This article will shed light on some of the core components i.e. Handler, Receiver, Setting up repository, Secret key, URI etc.  I love to work with WebHooks. The best part of Microsoft ASP.NET Webhook is that - it is open source.  One can easily get its source code for customization and update as per their own requirements.

Previous write-ups

There is a series of articles on Microsoft ASP.NET WebHooks. I recommend go-through all previous write-ups:

Pre-requisite

There are no major requirements to start with WebHooks, we just need to know:

  • Basics of Services (Web APIs etc.)
  • Visual Studio 2013 or later
  • Basic knowledge of ASP.NET
  • Basic knowledge of GitHub (repository control)

Purpose

Our main purpose of this article is to understand the various ways to work with Microsoft ASP.NET WebHooks.

To start working with WebHook is as easy as going and getting some bunnies. Generally, we can work with WebHook for two main purposes:

  • Setting up WebHook source repository
  • Consume WebHook

Setting up WebHook source repository

To add or customize WebHook, one should have complete source code. In this section, we will discuss how to setup repository to work for adding or customization WebHooks.

Setup source code

As we have already discussed that Microsoft ASP.NET WebHook is an open source solution and its source code is available over GitHub repository here: https://github.com/aspnet/WebHooks . If you are planning to add or customize WebHook, you should follow these, to setup repository:

  • Access https://github.com/ and login to your account.
  • Go to ASP.NET Webhook, repository url.
  • Fork repository and follow the instructions and you are good to go.
  • Use your favorite GUI or CLI to start, locally - I prefer to use GitBash or GitExtensions for windows.
  • Clone repository to your local system on your preferred location
  • Checkout dev branch for any addition/modification.
  • After any addition/modification make a Pull Request i.e. PR to main repository from where you forked your repository.

To create a new Pull request you just need to have a valid GitHub account and most importantly you have to sign the `cla for open source contribution'. For more information about cla please refer: https://cla2.dotnetfoundation.org/

Consume WebHooks

There are lots of WebHook Senders/ Receivers available, you can get started with one of these WebHooks using repository samples or creating your own custom application using nugget packages.

WebHook receiver Samples

Webhook receiver samples are available in GitHub repository. To use samples, you just need to go https://github.com/aspnet/WebHooks/tree/master/samples and select your sample to get started. Currently, available samples (for receivers) are:

All samples are referring to actual WebHook receiver projects and are part of ASP.Net WebHook solution. These are important when you are going to Debug actual source code.

Creating sample application using Nuget packages

All WebHooks are available in NuGet packages for this you just need to get the package from NuGet resource:

Parts of NuGet Packages

The NuGet packages have three modules as described:

What are WebHook Receivers?

A package/module created to receive WebHooks from others. The term Webhook Receivers is self-explanatory and tells that our WebHook receiver is developed to receive WebHooks from others. This purely depends upon the sender. Some senders require simple configurations while others may require complex configurations such as some extra configuration steps like registering, verifications, etc. This also depends upon security model the senders may have.

Some senders work on push-to-pull model and where HTTP Post request does not contain any data but contains reference to the event triggered. In that scenario, we might require some special things like on the basis of triggered event we need to pull data separately for our use.

Microsoft ASP.NET WebHooks are meant to both accept and verify WebHooks from particular senders. A single WebHook receiver can support more than one WebHooks as per their own configuration settings. In other words we can say that if we configured for ten Bitbucket repository for Webhook then our one BitBucket WebHook receiver can accept WebHooks from all these ten repositories.

Creating a WebHook Receiver

In this section, we will discuss in detail how to use BitBucket WebHook receivers in your own application. We will refer to a sample application and use NuGet packages instead of referring direct projects.

First of all lets go and create a receiver project, in this application we are going to use Visual Studio 2015.

  • Create new project from within your Visual Studio

  • Choose an empty project with Web API template

  • Install NuGet package using either Manage NuGet Package Manager Dialog or Package Manage Console.

  • Search Microsoft.AspNet.WebHooks.Receivers.BitBucket and make sure you have selected Include prerelease check box
  • Type Install-Package Microsoft.AspNet.WebHooks.Receivers.BitBucket -Pre in case you are using Package Manager Console.

You will notice that new assemblies added in the project- Microsoft.AspNet.WebHooks.Common, Microsoft.AspNet.WebHooks.Receivers, Microsoft.AspNet.WebHooks.Receivers.Bitbucket

Unique config key

A unique config key is nothing but a unique key for every WebHook receiver. A unique config key contains a secret_key and should not less than 32 and maximum than 128 characters long., which is nothing just a HMAC (https://en.wikipedia.org/wiki/Hash-based_message_authentication_code). There are lot of tools available to generate the secret key. In all examples, I used http://www.freeformatter.com/hmac-generator.html to generate HMAC or our WebHook receiver's secret_key.

The proceeding table tells the probable key of every receiver.

 

Unique Config key

Receiver

Key

BitBucket

MS_WebHookReceiverSecret_Bitbucket

Dropbox

MS_WebHookReceiverSecret_Dropbox

GitHub

MS_WebHookReceiverSecret_GitHub

Instagram

It required two keys:

MS_WebHookReceiverSecret_InstagramId - Client ID

MS_WebHookReceiverSecret_Instagram - Client Secrete

MailChimp

MS_WebHookReceiverSecret_MailChimp

MyGet

MS_WebHookReceiverSecret_MyGet

Slack

MS_WebHookReceiverSecret_Slack

Stripe

MS_WebHookReceiverSecret_Stripe

VSTS

MS_WebHookReceiverSecret_VSTS

Zendesk

MS_WebHookReceiverSecret_Zendesk

Setup unique config key

To setup a unique config key with a secret_key value, we need to add a config key with a unique name. In our case we are going to add MS-WebHookReceiverSecret_Bitbucket

In continuation to setup our application to receive proper notification with a unique config key which contains a secret_key value, just open Web.Config of our newly created application and add key here, as we have already discussed that every WebHook Receiver has a unique key, so, if you are thinking to create a new application based on WebHook receiver other than Bitbucket receiver, please refer to preceding table which outlines available unique keys for all WebHook receivers.

<appSettings>
 <add key="MS_WebHookReceiverSecret_Bitbucket" value="add_Secret_key_for_Bitbucket"/>
</appSettings>

Initialize or Configuring WebHook receiver

Till now, we have created a small application to consume a Bitbucket WebHook receiver, setup a unique key to get event notification from particular WebHook.

Next step is to initialize a WebHook receiver, open WebAPIConfig file from your project and modify by adding: config.InitializeReceiveBitbucketWebHooks().

WebHook receivers configurable using IWebHookReceiveConfig interface and can be implement using any dependency injection.

namespace BitBucketSampleReceiverApp
{
 public static  class WebApiConfig
 {
 public static  void Register(HttpConfiguration config)
 {
 // Web API configuration and services
 // Web API routes
 config.MapHttpAttributeRoutes();
 config.Routes.MapHttpRoute(
 name: "DefaultApi",
 routeTemplate: "api/{controller}/{id}",
 defaults: new  { id = RouteParameter.Optional }
 );
 // Initialize Bitbucket WebHook receiver
config.InitializeReceiveBitbucketWebHooks();
 }
 }
}

Creating WebHook Handler

A WebHook handler is nothing just a simple class with a special operation.

Next step is to create a handler to receive event notifications from BitBucket, for this we need to follow:

  • Add new folder and name it: WebHooks - you can put any kind of webhook-handlers under this folder
  • Add new class under folder WebHooks and  name it: BitBucketWebHookhandler.cs it should be inherited from WebHookHandler
  • In this sample, we are going to track push notifications, let's write code to get push notifications. However, we can write a code to get any kind of event notifications.
  • We can write our application to receive any kind of event notification from Bitbucket, if we have made settings for those event notifications.
  • Documentation of BitBucket push notifications can be found here: https://confluence.atlassian.com/bitbucket/event-payloads-740262817.html#EventPayloads-Push
public class  BitBucketWebHookhandler : WebHookHandler
 {
 public BitBucketWebHookhandler()
 {
 Receiver = BitbucketWebHookReceiver.ReceiverName;
 }
 public override  Task ExecuteAsync(string receiver, WebHookHandlerContext context)
 {
 var dataJObject = context.GetDataOrDefault<JObject>();
 var action = context.Actions.First();
 switch (action)
 {
 case BitBucketAction.Push:
 var repository = dataJObject["repository"].ToObject<BitbucketRepository>();
 var actor = dataJObject["actor"].ToObject<BitbucketUser>();
 AssessChanges(dataJObject);
 break;
 default:
 var data = dataJObject.ToString();
 break;
 }
 return Task.FromResult(true);
 }
 private static  void AssessChanges(JObject dataJObject)
{
 foreach (var change in dataJObject["push"]["changes"])
 {
 var previousValue = change["old"]["target"].ToObject<BitbucketTarget>();
 var newValue = change["new"]["target"].ToObject<BitbucketTarget>();
 }
 }
}

In preceding code listing, we have our handler which is specifically for handling BitBucket events. If you see Receiver in the constructor, this receiver will return BitBucket only.

Our BitBucketWebHookHandler class inherits from WebHookHandler (an abstract class). Here, we override ExecuteAsync method which has the receiver and WebHookhandlerContext (context of incoming) method.

In preceding code-listing we are getting data as per the documentation of BitBucket mentioned here: https://confluence.atlassian.com/bitbucket/event-payloads-740262817.html#EventPayloads-Push

In case, where we have a single generic handler for multiple WebHook receivers, we make check for particular receiver like: if("BitBucket".Equals(receiver, StringComparison.CurrentCultureIgnoreCase)). Our modified code would be look like:

namespace BitBucketSampleReceiverApp.WebHooks
{
 public class  BitBucketWebHookhandler : WebHookHandler
 {
 public BitBucketWebHookhandler()
 {
 Receiver = BitbucketWebHookReceiver.ReceiverName;
 }
 public override   Task ExecuteAsync(string receiver, WebHookHandlerContext context)
 {
 if ("BitBucket".Equals(receiver, System.StringComparison.CurrentCultureIgnoreCase))
 {
 ReceivefromBitbucket(context);
 }
 return Task.FromResult(true);
 }

In preceding code listing we have extracted our main method to a small method ReceivefromBitbucket(), similarly we can add more conditions for different WebHook receivers while working with a generic receiver.

Publishing application

We have created a receiver application with a Bitbucket handler. Our small application is now capable to talk with Bitbucket or in other words, it can now listen or receive event notifications from Bitbucket repository for which we have configured the WebHook.

Why we need public URI with SSL support?

Due to security reasons almost all WebHook providers require that URI should be public and with SSL support so, it only uses `https' protocol. In other words, we can say that if our URI is not public then Bitbucket repository will not be able to talk with our local URI.

How to get public URI?

As we discussed in preceding section that to receive notifications our application should have public URI with https protocol i.e. it should be SSL enabled. There are lot of ways to make your application public with SSL like:

  • Purchase a domain
  • Purchase a hosting
  • Purchase a SSL certificate
  • Deploy your application
  • Bind your domain with SSL

These steps require money and lot of time. The easiest way to achieve our deployment is with the use of Azure. Azure URLs have internally SSL enabled so we can use those for our application. We are not going to discuss in detail about Azure deployment here.

Configure Bitbucket repository

We have already created a BitbucketWebHook receiver application which will talk with Bitbucket repository and listen to event notifications for us.

Next step would be to configure our Bitbucket repository which will send event notifications to our BitBucketWebHook receiver.

  • WebHook URI format is: https://<host>/api/webhooks/incoming/bitbucket/?code=secret_key
  • secret_key should contain same value as MS_WebHookReceiverSecret_bitbucket key in configurations

URI of WebHook receivers

All Webhook providers need different information on the time of configuration of WebHooks. You might require different URI for different WebHook receiver, proceeding table describes all about different URI formats for various WebHook receivers.

URI formats for various WebHook receivers

Receiver

URI format

BitBucket

https://<host>/api/webhooks/incoming/bitbucket/?code=secret_key

Dropbox

https://<host>/api/webhooks/incoming/bitbucket/?{id}

GitHub

https://<host>/api/webhooks/incoming/bitbucket/{id}?code=secret_key

Instagram

https://<host>/api/webhooks/incoming/instagram/{id}

MailChimp

https://<host>/api/mailchimp/?code=secret_key

MyGet

https://<host>/api/webhooks/incoming/myget?code=secret_key

Slack

https://<host>/api/webhooks/incoming/slack/{id}

Stripe

https://<host>/api/webhooks/incoming/stripe/{id}?code=secret_key

VSTS

https://<host>/api/webhooks/incoming/vsts?code=secret_key

Zendesk

https://<host>/api/webhooks/incoming/zendesk/{id}?code=secret_key

A typical URI would be in the form of:

https://<host>/api/webhooks/incoming/<receiver>/{id}

Where:

  • <host> is the public url of your application
  • <receiver> is the name of the receiver, like in our example it is bitbucket
  • {id} is an identifier and optional in most of cases, which can be used to identify a particular WebHook receiver configuration.

Assigning Secret_key value

We are all set to go, just one thing we still require to set i.e. Secret Key, In our webconfig.cs we have already added key MS_WebHookReceiverSecret_Bitbucket but without value.  Now our next step is to assign a value to our already added config key. There is no need to add value in our application (you can also add value to your webconfig at the time of creation of application) as we have already deployed the application. If we are going to edit webconfig file then we need to re-deploy our application, which is a really makes no sense. Here, we can use the power of Azure to add config key. To do so, go over to our Azure portal and access Application settings, add key and value as shown in proceeding image.

How WebHooks Process

As soon as WebHook receiver validate the WebHooks request , it tells the application or user code to process. In other words, first WebHook receiver validate the Webhook requests and Webhook Handlers start processing.

What is WebHook handler?

A WebHookHandler is an abstract class and implements IWebHookHandler interface. While we create the application or our handler like Bitbucket handler in our case, do not use IWebhookhandler interface but directly use WebHookHandler abstract class.

WebHook request can be processed by one or more handlers which are called in order based with the use of their own order property from lowest to highest (between 1 to 100)

Order is an integer and DefaultOrder value is 50.

Why to set response for WebHook handler?

There might be some scenario where we need to set back response to originating API. To do so, we can set the Response property for a handler. A best example to set Response property would be HTTP Status code 410 - Gone. This indicates that WebHook is not active and no further requests should be entertained.

Response property is an optional.

Verify WebHook receiver application

Finally, we are ready to test our WebHook receiver. Go and make few changes to any of your files locally of Bitbucket repository that you have configured to send Webhook and then push it over to remote repository. We can verify the event notification in two ways.

  1. By verification from Bitbucket `View request' page.
  2. Verify the incoming data on application - can Debug remotely

Verification from Bitbucket

To make it easier let us edit event notifications for that go back to WebHook settings of your Bitbucket repository and set to receive events/notifications for Push and Issues.

Now, go back to repository and create a new issue or edit few changes in an existing issue. It will trigger particular event notification.

You can verify this by visiting Settings->WebHook->View from within your bitbucket repository.

I have created one Issue and then commented, in proceeding image, you can see what event triggered and what the details is. For more details click on View details link.  On details page, you can get all details of the request viz. HTTP status, Elapsed time, Request time etc. You can also get the request and body details.

Debug application

We have verified that our WebHook is working fine by using Bitbucket settings page, where we saw that which event triggered and what we received. In real-time scenarios no one ever think to come back on Bitbucket setting pages and see the details. But, in real-time we would more interested how to consume the result, we are getting back from those triggered events in other words what our Webhook receiver has received.

So, let check whether our application is receiving the things or not? Go and attach Debugger to application and make few changes in your repository.

  • In this section we are not going to discuss how to debug an azure application. This part is beyond the subject of this boo.
  • Debug cloud services on virtual machines to know more about debugging an Azure app.

To verify our app, I have just modified ReadMe file and then pushed the changes to remote repository. Here, Push notification triggered and notifies our WebHook receiver so, our application should hit the Debug point as picturized in proceeding image. We should get the triggered data.

Here, we received everything we need. So, our WebHook is working fine. In this application, we verified and tried to understand how our WebHook receiver talks with Bitbucket repository and how we get the requested data in our application. In this application we did not use the data but in actual environment where we need to utilize hooks, one should use the data. In proceeding section we will create a simple application to consume or use the data received.

Conclusion

In this article we discussed about setting up repository for Microsoft ASP.NET WebHooks, we also discussed consuming WebHooks to work with Bitbucket activities.

Further readings