I like when integrations like Twilio are simple and straightforward. I recently had the privilege of implementing it for my company. For my implementation I have a couple of special requirements that I think makes my solution better than just referencing a NuGet package and calling MessageResource.Create.
My requirements require creating user controlled text messaging templates and customer controlled enabling/disabling of one or more text messages. Let’s get started.
Using Twilio with C# and MVC
I’m going to begin by describing the code hierarchy I plan to implement. The pattern follows how I implemented and described in my book ASP.NET MVC with Bootstrap and Knockout.js where I describe a project flow of Controller -> Orchestration -> Service -> Repository. From services there is also a flow to what I call Behaviors.
The controller – TextMessageController.cs – will be the starting point. It will accept information about the customer and what message should be sent. In this example it will be a “welcome message”. This controller will use a customer ID and fetch the customer’s phone number.
The controller will then call the TextMessageOrchestration that accepts the message type and the customer’s phone number. The message type will be an enum. The orchestration will be responsible for 3 things:
- Fetching the text message template from the database
- Calling a behavior to confirm that the account and customer are configured and wish to receive text messages
- Call my Twilio service to send the templated message
In my code I’ve setup automatic dependency injection with using Ninject which is why you’ll see interfaces being auto instantiated by constructor variables.
[code]
using System.Linq;
using Behaviours.Handlers;
using ExternalApiClients.Twilio;
using Model;
using Services;
namespace Orchestrations.Implementation
{
public class TxtMessageOrchestration : ITxtMessageOrchestration
{
private readonly IAccountService _accountService;
private readonly ITwilioApiClient _twilioApiClient;
private readonly ITxtEnabledHandler _txtEnabledHandler;
public TxtMessageOrchestration(IAccountService accountService, ITwilioApiClient twilioApiClient, ITxtEnabledHandler txtEnabledHandler)
{
_accountService = accountService;
_twilioApiClient = twilioApiClient;
_txtEnabledHandler = txtEnabledHandler;
}
public void Send(TxtType txtType, string phoneNumber)
{
var txtTemplate = _accountService.GetTextTemplate(txtType);
if (_txtEnabledHandler.Enabled(txtTemplate))
{
_twilioApiClient.Send(txtTemplate.TwilioSid, phoneNumber,
txtTemplate.TxtBody);
}
}
}
public enum TxtType {
WelcomeMessage,
AnyOtherType
}
}
[/code]
The TxtEnabledHandler is quite simple where it checks that the txtTemplate.Enabled is true. For my requirements I have a lot more other settings that I’ve split this into a behavior where I wrote over 40 unit tests for the 5 or 6 different combinations of settings that identify whether to send or not. You can also explore leveraging the JavaScript Fetch API if you’re not able to use C#.
And finally, let’s look at the TwilioApiClient:
[code]
using System;
using System.Configuration;
using System.Web;
using Twilio;
using Twilio.Rest.Api.V2010.Account;
namespace ExternalApiClients.Twilio.Implementation
{
public class TwilioApiClient : ITwilioApiClient
{
public TwilioApiClient()
{
TwilioClient.Init(ConfigurationManager.AppSettings[“Twilio.ApplicationKey”], ConfigurationManager.AppSettings[“Twilio.ApplicationSecret”]);
}
public void Send(string twilioSid, string phoneNumber, string txtBody)
{
var url = new Uri(string.Format(“https://{0}/v1/TwilioListener”, HttpContext.Current.Request.Url.Host));
MessageResource.Create(
body: txtBody,
to: phoneNumber,
messagingServiceSid: twilioSid,
statusCallback: url
);
}
}
}
[/code]
I’ve setup a webhook listener for a controller called TwilioListener that I’ve set as the callback URL so I can read responses from Twilio. This code requires the Twilio NuGet package.
That’s it, I now have an excellent structure to add many different text message templates with only a single dependent class on Twilio which in a worse-case scenario would be easy to swap out.