VB.NET Webhook Tutorial

This tutorial will take you through the ENTIRE process of creating a web hook receiver in VB and ASP.NET. It can be easily ported to C#, if you're not old like me. 

It's safe to say that this tutorial does not exist anywhere on the internet. I've looked. 

I'm going to code two of them in the next few days, this first one will receive a webhook sent from www.github.com, because it's easy, and easy to test repeatedly. I can just click a button on Github and it will send my webhook over and over. 

The second one will be using the 'custom' API, it's going to come from a national bank. 

Step one is included here, in the 'teaser' section, because many of you are not subscribers and I want you to know that I'm EASILY going to save you days of work here. I just spent 3 days figuring this out. 

So... 

Step 1:

Create a new ASP.NET Web Application (.NET Framework) (mine is in VB, but it can be easily ported to C#)

 

Step 2 is below. 

Related Articles

... and you 'll find more on the ApisAndWebhooks Menu

Next

Empty. 

Open the NuGet Package Manager

Add these

 

Make your project look like this. Add two classes (WebApiConfig and GitHubWebHookHandler) and Global.asax.

Add the Default page as a standard web page, and set it to 'set as default page'. This isn't really necessary, but it'll give the app a place to open to if you run it in the dev environment.

Web.config

Note: The code shown does not handle security, or this key, that's an additional complexity and we'll take that on later

<configuration>
  <appSettings>
    <add key="MS_WebHookReceiverSecret_GitHub" value="xxxxxxxxxxxx0c780e49a5c72972590571fd8" />
  </appSettings>
  <system.web>

Global.asax

Imports System.Web.Http
Imports System.Web.SessionState
 
Public Class Global_asax
    Inherits System.Web.HttpApplication
 
    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        'log the app opening
        Dim logName = "Application"
        Dim objEventLog As EventLog = New EventLog()
        objEventLog.Source = logName
        objEventLog.WriteEntry("global_asax", EventLogEntryType.Warning)
 
        'this is a delegate, apparantly
        System.Web.Http.GlobalConfiguration.Configure(AddressOf WebApiConfig.Register)
 
    End Sub
End Class

WebApiConfig

Imports System
Imports System.IO
Imports System.Net
Imports System.Web
Imports System.Web.Http
Imports FPCommon
 
Module WebApiConfig
    Sub Register(ByVal config As System.Web.Http.HttpConfiguration)
        Dim strError As String = ""
        Try
            'this is called att app start from Global.asax
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3
 
            config.MapHttpAttributeRoutes()
 
            config.Routes.MapHttpRoute(name:="DefaultApi", routeTemplate:="api/{controller}/{id}", defaults:=New With {Key .id = RouteParameter.[Optional]})
            config.InitializeReceiveGitHubWebHooks()
 
        Catch ex As Exception
            ErrorHandler.globalErrorHandler(ex, strError, "vb", "sg", True)
        End Try
 
 
    End Sub
 
End Module

GitHubWebHookHandler

Imports System
Imports System.IO
Imports System.Net
Imports System.Threading.Tasks
Imports Microsoft.AspNet.WebHooks
Imports Newtonsoft.Json.Linq
 
Public Class GitHubWebHookHandler
    Inherits WebHookHandler
 
    Public Sub New()
        Dim strError As String = ""
        Try
            Me.Receiver = Microsoft.AspNet.WebHooks.GitHubWebHookReceiver.ReceiverName
 
            Dim strSubject As String = String.Format("GitHubWebHookHandler New {0:hh:mm:ss}", Now)
            MailHandler.sendMail("credit@ngabrick.com", "steve@4penny.net", "", "", "webhook api notice", strSubject, MailHandler.ErrorHandlingTypeType.Ignore, "", "mail.ngabrick.com", False)
 
        Catch ex As Exception
            Throw ex
        End Try
 
    End Sub
 
    Public Overrides Function ExecuteAsync(ByVal generator As String, ByVal context As WebHookHandlerContext) As Task
        Dim strError As String = ""
        Try
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3
 
            Dim entry As JObject = context.GetDataOrDefault(Of JObject)()
 
            '"entry" is a JSON object, handle the incoming data here
 
        Catch ex As Exception
            Throw ex
        End Try
 
 
        Return Task.FromResult(True)
    End Function
End Class

 

Now, go over to GitHub

Create a free account, if you need to

Add a repo, if you need to

Navigate to the repository

Click on Settings, then Webhooks

Fill this in as shown below. Your code needs to be public on the internet. It is possible to test without publishing to the internet... but that's a different article. Notice the 'secret' area. Github sends a 'secret' (they look like GUIDs) and the .NET app receives the secret and that way you know that someone is not spoofing you. This article does not handle the security, that's the next article. 

You can set yourself up for as many notifications as you'd like, but it's quick and easy to set yourself up to receive 'stars'

 

 

This is the result. If you've coded all the above correctly, when you click the 'star' in Github you'll get the text below in the ExecuteAsync method. Find this line of code and handle the data. I didn't include it in the sample code because you won't want to do it my way. Recommend that you deserialize the JSON and then store it somehow in a database. 

Dim entry As JObject = context.GetDataOrDefault(Of JObject)()

 

Resulting JSON

{

  "action": "deleted",

  "starred_at": null,

  "repository": {

    "id": 762457507,

    "node_id": "R_kgDOLXItow",

    "name": "Compare-Net-Objects",

    "full_name": "sgray128/Compare-Net-Objects",

    "private": false,

    "owner": {

      "login": "sgray128",

      "id": 161059510,

      "node_id": "U_kgDOCZmStg",

      "avatar_url": "https://avatars.githubusercontent.com/u/161059510?v=4",

      "gravatar_id": "",

      "url": "https://api.github.com/users/sgray128",

      "html_url": "https://github.com/sgray128",

      "followers_url": "https://api.github.com/users/sgray128/followers",

      "following_url": "https://api.github.com/users/sgray128/following{/other_user}",

      "gists_url": "https://api.github.com/users/sgray128/gists{/gist_id}",

      "starred_url": "https://api.github.com/users/sgray128/starred{/owner}{/repo}",

      "subscriptions_url": "https://api.github.com/users/sgray128/subscriptions",

      "organizations_url": "https://api.github.com/users/sgray128/orgs",

      "repos_url": "https://api.github.com/users/sgray128/repos",

      "events_url": "https://api.github.com/users/sgray128/events{/privacy}",

      "received_events_url": "https://api.github.com/users/sgray128/received_events",

      "type": "User",

      "site_admin": false

    },

    "html_url": "https://github.com/sgray128/Compare-Net-Objects",

    "description": null,

    "fork": false,

    "url": "https://api.github.com/repos/sgray128/Compare-Net-Objects",

    "forks_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/forks",

    "keys_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/keys{/key_id}",

    "collaborators_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/collaborators{/collaborator}",

    "teams_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/teams",

    "hooks_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/hooks",

    "issue_events_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/issues/events{/number}",

    "events_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/events",

    "assignees_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/assignees{/user}",

    "branches_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/branches{/branch}",

    "tags_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/tags",

    "blobs_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/git/blobs{/sha}",

    "git_tags_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/git/tags{/sha}",

    "git_refs_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/git/refs{/sha}",

    "trees_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/git/trees{/sha}",

    "statuses_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/statuses/{sha}",

    "languages_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/languages",

    "stargazers_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/stargazers",

    "contributors_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/contributors",

    "subscribers_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/subscribers",

    "subscription_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/subscription",

    "commits_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/commits{/sha}",

    "git_commits_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/git/commits{/sha}",

    "comments_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/comments{/number}",

    "issue_comment_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/issues/comments{/number}",

    "contents_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/contents/{+path}",

    "compare_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/compare/{base}...{head}",

    "merges_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/merges",

    "archive_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/{archive_format}{/ref}",

    "downloads_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/downloads",

    "issues_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/issues{/number}",

    "pulls_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/pulls{/number}",

    "milestones_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/milestones{/number}",

    "notifications_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/notifications{?since,all,participating}",

    "labels_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/labels{/name}",

    "releases_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/releases{/id}",

    "deployments_url": "https://api.github.com/repos/sgray128/Compare-Net-Objects/deployments",

    "created_at": "2024-02-23T20:28:33Z",

    "updated_at": "2024-02-29T15:28:38Z",

    "pushed_at": "2024-02-23T21:37:59Z",

    "git_url": "git://github.com/sgray128/Compare-Net-Objects.git",

    "ssh_url": "git@github.com:sgray128/Compare-Net-Objects.git",

    "clone_url": "https://github.com/sgray128/Compare-Net-Objects.git",

    "svn_url": "https://github.com/sgray128/Compare-Net-Objects",

    "homepage": null,

    "size": 11875,

    "stargazers_count": 0,

    "watchers_count": 0,

    "language": "C#",

    "has_issues": true,

    "has_projects": true,

    "has_downloads": true,

    "has_wiki": true,

    "has_pages": false,

    "has_discussions": false,

    "forks_count": 0,

    "mirror_url": null,

    "archived": false,

    "disabled": false,

    "open_issues_count": 0,

    "license": {

      "key": "ms-pl",

      "name": "Microsoft Public License",

      "spdx_id": "MS-PL",

      "url": "https://api.github.com/licenses/ms-pl",

      "node_id": "MDc6TGljZW5zZTE5"

    },

    "allow_forking": true,

    "is_template": false,

    "web_commit_signoff_required": false,

    "topics": [],

    "visibility": "public",

    "forks": 0,

    "open_issues": 0,

    "watchers": 0,

    "default_branch": "master"

  },

  "sender": {

    "login": "sgray128",

    "id": 161059510,

    "node_id": "U_kgDOCZmStg",

    "avatar_url": "https://avatars.githubusercontent.com/u/161059510?v=4",

    "gravatar_id": "",

    "url": "https://api.github.com/users/sgray128",

    "html_url": "https://github.com/sgray128",

    "followers_url": "https://api.github.com/users/sgray128/followers",

    "following_url": "https://api.github.com/users/sgray128/following{/other_user}",

    "gists_url": "https://api.github.com/users/sgray128/gists{/gist_id}",

    "starred_url": "https://api.github.com/users/sgray128/starred{/owner}{/repo}",

    "subscriptions_url": "https://api.github.com/users/sgray128/subscriptions",

    "organizations_url": "https://api.github.com/users/sgray128/orgs",

    "repos_url": "https://api.github.com/users/sgray128/repos",

    "events_url": "https://api.github.com/users/sgray128/events{/privacy}",

    "received_events_url": "https://api.github.com/users/sgray128/received_events",

    "type": "User",

    "site_admin": false

  }

}

 


RealWorldCode gives developers practical, real‑world solutions with clean, working code — no fluff, no theory, just answers.
Links
Home
Knowledge Areas
Sitemap
Contact
Et cetera
Privacy Policy
Terms and Conditions
Cookie Preferences