How to write a webhook in ColdFusion

This post outlines how I am capturing a webhook from GitLab CI (a notification as to when a build is completed, and its details) and converting it to be a webhook to push to Slack. This is a specific task I completing here, but what I expect for you to able to abstract from here is this:
1. How to capture a webhook in ColdFusion
2. How to send a webhook from ColdFusion

For quick and easy deployment, I decided to use an existing server. So I installed a nginx railo stack on an existing ubuntu box for this.

Capturing the Webhook

Webhooks come in in one of two ways. Either as a variable named payload, or as the body in the request. I am expecting the payload to be a JSON string. In either case, we want to capture the incoming hook.

_payload = "{}";  
if (structKeyExists(URL,"payload")) {  
    // Get payload from GET Request
    _payload = URL.payload;
} else if (structKeyExists(FORM,"payload")) {
    // Get payload from POST Request
    _payload = FORM.payload;
} else {
    // Get payload from the Body of the Request
    _payload = getHttpRequestData().content;
}
// now turn our payload into a structure 
_requestpayload = deserializeJSON(_payload);  

From this, we have captured the payload from our request and are able to work with it as a normal ColdFusion structure.

Sending a Webhook

Sending a webhook to another server or service is relatively easy as well. First thing you should do is find out how your target is expecting to receive a webhook. In most cases, they will be waiting for a JSON POST with either a variable named payload containing the JSON, or they are expecting the JSON payload to be the body of the Request you are sending. In the case of slack, they will accept both of these methods (as of the time of this writing).

So we start by creating our payload. For ease of use, I like to work with structures, and then convert those structures to JSON at the time I am ready to send.

// Build the payload
_payload = {  
    "fallback" = "Testing my webhook",
    "text" = "Webhook Test",
    "pretext" = "This message is the result of a tested webhook"
};
// Setup and Send the Request 
httpServer = new http()  
httpServer.setMethod("POST");  
httpServer.setCharset("utf-8");  
httpServer.addParam( type="header", name="Content-Type", value="application/x-www-form-urlencoded; charset=UTF-8" );

// Note: Railo doesn't change the case of the keys when using serializeJSON()
// Adobe ColdFusion will usually UCASE all the keys.  
// Additional formatting may be required.
_formattedPayload = serializeJSON(slackmessage);  
httpServer.addParam( type="body", value=_formattedPayload );  
httpServer.setURL("<slack webhook url>");  
httpServer.send();  

once you run this script, you should see your webhook message in slack.

In Use

I have written a utility to capture a payload from GitLab CI, and from that, create a payload to broadcast to Slack.

if (structKeyExists(URL,"payload")) {  
    // Get payload from GET Request
    _payload = URL.payload;
} else if (structKeyExists(FORM,"payload")) {
    // Get payload from POST Request
    _payload = FORM.payload;
} else {
    // Get payload from the Body of the Request
    _payload = getHttpRequestData().content;
}
// now turn our payload into a structure 
_requestpayload = deserializeJSON(_payload);

// Build the payload
_payload = {  
    "fallback" = "#_requestpayload.project_name# #_requestpayload.ref# Build Completed: #_requestpayload.build_status#",
    "color":((_requestpayload.build_status == "success")?"good":"danger"),
    "pretext": "Repository: #_requestpayload.project_name# | <__URL__|See Details>",
    "fields": [
        {
            "title": "Status",
            "value": "#_requestpayload.build_status#",
            "short": true
        },
        {
            "title": "Developer",
            "value": "#_requestpayload.push_data.user_name#",
            "short": true
        },
        {
            "title": "Project",
            "value": "#_requestpayload.project_name#",
            "short": true
        },
        {
            "title": "Branch",
            "value": "#branchName#",
            "short": true
        },
        {
            "title": "sha",
            "value": "#_requestpayload.sha#",
            "short": false
        }
    ]
};
// Setup and Send the Request 
httpServer = new http()  
httpServer.setMethod("POST");  
httpServer.setCharset("utf-8");  
httpServer.addParam( type="header", name="Content-Type", value="application/x-www-form-urlencoded; charset=UTF-8" );  
httpServer.addParam( type="body", value=serializeJSON(slackmessage) );  
httpServer.setURL("<slack webhook url>");  
httpServer.send();  

Conclusion

Capturing and sending webhooks is not a complex task in of itself. If something does not work while trying to send a webhook, the issue is more then likely a problem which either how you are sending it (GET, POST, Request Body, Destination URL), or with the content of the payload itself.

Be sure to double check the documentation of the target with how they are expecting to receive a webhook, and double check that you are sending it to the correct location.

Apart form that, Happy WebHooking!