

What's new in the Twilio helper library for ASP.NET (v6.0.0 - August 2022)
source link: https://www.twilio.com/blog/whats-new-in-twilio-helper-library-for-aspnet-v6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

What's new in the Twilio helper library for ASP.NET (v6.0.0 - August 2022)

The Twilio helper library for ASP.NET (Twilio.AspNet) is a community-driven open-source project to make integrating Twilio with ASP.NET easier, for both ASP.NET Core and ASP.NET MVC on .NET Framework. The library helps you achieve common use cases and with the release of version 6, we're expanding the library's capabilities and improving existing features.
Wondering what was previously introduced? You can read about v5.73.0 and prior releases here.
What's new in Twilio.AspNet v6.0.0
V6.0.0 is a major release of the Twilio.AspNet library because it contains breaking changes. Starting from this release, version numbers do not match the version of the Twilio .NET SDK.
Here's an overview of the changes:
🎉 NEW FEATURES
- You can now add the
TwilioRestClient
to ASP.NET Core's dependency injection (DI) container, using the.AddTwilioClient
method. This Twilio client will use anHttpClient
provided by the HTTP client factory. You can find more information about this new feature later in the article.
🙌 ENHANCEMENTS
- Big breaking change to the
[ValidateRequest]
attribute. The attribute no longer accepts parameters or properties. Instead, you have to configure the request validation elsewhere. In ASP.NET Core, you can configure request validation using .NET Configuration or using code. In ASP.NET MVC on .NET Framework, you can configure request validation in the Web.config file using thetwilio/requestValidation
configuration element, or using app settings. You can find more information about this enhancement later in the article. - We migrated the build process from AppVeyor to GitHub Actions. You can now see how we build, test, package, and push the release to nuget.org using the workflows under the Actions tab in GitHub
- The README.md file has been updated to document the new features and improvements.
Before v6.0.0, there was another small release, v5.77.0 which included the following changes:
- Twilio.AspNet.Core and Twilio.AspNet.Common now use .NET Standard 2.0 and dropped older .NET Standard versions.
- Twilio.AspNet.Mvc now targets .NET 4.6.2.
- The Microsoft.AspNetCore.Mvc.Core dependency has been updated to a more recent version. For newer versions of .NET, a framework dependency is used instead. This updated dependency also updated the transitive dependency on Microsoft.AspNetCore.Http. This was done because the old version of Microsoft.AspNetCore.Http contained a vulnerability (CVE-2020-1045).
- Twilio.AspNet.Core and Twilio.AspNet.Mvc now depend on version 5.77.0 of the Twilio package.
Add Twilio API client to the dependency injection container
Background on TwilioClient and TwilioRestClient
The Twilio .NET SDK provides a static TwilioClient
class to authenticate:
using Twilio;
using Twilio.Types;
using Twilio.Rest.Api.V2010.Account;
string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");
TwilioClient.Init(accountSid, authToken);
This is the simplest way to authenticate, but it is better to use API keys as documented here.
Once initialized, you can interact with the API resource classes, for example, here's how you can send an SMS using the MessageResource
class.
MessageResource.Create(
body: "Ahoy!",
from: new PhoneNumber("+15017122661"),
to: new PhoneNumber("+15558675310")
);
This is the quick and easy approach you will find in most of our documentation, however, there's another way. Instead of using the static TwilioClient
class, you can create a TwilioRestClient
and pass it in as a parameter when interacting with the API resource classes.
Here's what that would look like for the same SMS example:
var twilioClient = new TwilioRestClient(accountSid, authToken);
MessageResource.Create(
body: "Ahoy!",
from: new PhoneNumber("+15017122661"),
to: new PhoneNumber("+15558675310"),
client: twilioClient
);
You may be wondering, why would you want to use the TwilioRestClient
instead of the static TwilioClient
?
- You cannot have multiple instances or any instances of a static class. So if your application needs to authenticate with multiple Twilio accounts, like in a multi-tenant app, you cannot do that with the static
TwilioClient
, but you can support this scenario using theTwilioRestClient
. - .NET developers generally don't like using static classes because it makes their code less maintainable and harder to test. By using the
TwilioRestClient
class and theITwilioRestClient
interface, you can use the inversion of control (IoC) principle and more easily mock the clients which makes it easier to test.
The static TwilioClient
class uses the TwilioRestClient
class itself, but it stores it as a singleton.
New: Add TwilioRestClient as a service
Now that you know how to use the TwilioRestClient
and why you'd want to, let's take a look at the new feature that is introduced in v6.0.0.
You can now add the TwilioRestClient
to ASP.NET Core's services using the .AddTwilioClient
method:
using Twilio.AspNet.Core;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTwilioClient();
Once the client is added, you can request ITwilioRestClient
and TwilioRestClient
via dependency injection. For example, here's how you can receive the client via the constructor of a controller, and then send the same SMS as before:
using Microsoft.AspNetCore.Mvc;
using Twilio.Clients;
using Twilio.Rest.Api.V2010.Account;
using Twilio.Types;
public class SmsController : Controller
{
private readonly ITwilioRestClient twilioClient;
public SmsController(ITwilioRestClient twilioClient)
{
this.twilioClient = twilioClient;
}
public async Task<IActionResult> Send()
{
await MessageResource.CreateAsync(
body: "Ahoy!",
from: new PhoneNumber("+15017122661"),
to: new PhoneNumber("+15558675310"),
client: twilioClient
);
return Ok();
}
}
Constructors aren't the only place you can inject dependencies. You can inject dependencies into controller actions, views, endpoints, and more.
This feature is only for ASP.NET Core and will not be added into ASP.NET on .NET Framework because there's no built-in DI container in the old framework.
Before you can run this code, you need to configure the Twilio client, which is done through standard .NET configuration. Here's what the configuration would look like in JSON:
{
"Twilio": {
"Client": {
"AccountSid": "[YOUR_ACCOUNT_SID]",
"AuthToken": "[YOUR_AUTH_TOKEN]",
"ApiKeySid": "[YOUR_API_KEY_SID]",
"ApiKeySecret": "[YOUR_API_KEY_SECRET]",
"CredentialType": "[Unspecified|AuthToken|ApiKey]",
"Region": null,
"Edge": null,
"LogLevel": null
}
}
}
A couple of notes:
- You can configure
Twilio:AuthToken
whichTwilio:Client:AuthToken
will fall back to. Twilio:Client:CredentialType
has the following valid values:Unspecified
,AuthToken
, orApiKey
.- When using
AuthToken
, you only need to configure theAccountSid
and theAuthToken
. - When using
ApiKey
, you only need to configure theApiKeySid
and theApiKeySecret
.
- When using
Twilio:Client:CredentialType
is optional and defaults toUnspecified
.- If
Unspecified
, whether you configured an API Key or an Auth Token will be detected. - If
Unspecified
and the API Key and Auth Token are both configured, the API Key will be used as it is the preferred way to authenticate.
- If
While the example above shows the JSON representation of the configuration, you can use any configuration provider plugged into your application. The AuthToken
and ApiKeySecret
in particular should not be stored in your appsettings.json file, but in your user-secrets, in a vault service, or in environment variables instead.
If you do not wish to configure the Twilio client using configuration, you can do so through code:
builder.Services
.AddTwilioClient((serviceProvider, options) =>
{
options.AccountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
options.AuthToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");
options.ApiKeySid = Environment.GetEnvironmentVariable("TWILIO_API_KEY_SID");
options.ApiKeySecret = Environment.GetEnvironmentVariable("TWILIO_API_KEY_SECRET");
options.Edge = null;
options.Region = null;
options.LogLevel = null;
options.CredentialType = CredentialType.Unspecified;
});
Do not hard-code your Auth Token or API Key secret into code and do not check them into source control. We recommend using the Secrets Manager for local development. Alternatively, you can use environment variables, a vault service, or other more secure techniques.
Improved HTTP client
TwilioClient
and TwilioRestClient
use the HttpClient
class to send HTTP requests to the Twilio APIs.
Unless you pass in your own HTTP client to the TwilioRestClient
constructor, it will create a new HttpClient
for every instance. Since the TwilioClient
holds on to a singleton of TwilioRestClient
, TwilioClient
will use a single HttpClient
until you restart your application which can lead to DNS stalenes. On the other hand, if you create a new TwilioRestClient
for every API call, a new HttpClient
will also be created for every API call which could lead to socket exhaustion.
By using TwilioRestClient
instead of the static TwilioClient
, you already avoid the DNS staleness issues, but you could still run out of network sockets. To avoid this issue and improve performance, .AddTwilioClient
uses the HTTP client factory APIs to retrieve an HttpClient
and supplies it to the TwilioRestClient
constructor.
In short, using the TwilioRestClient
configured by .AddTwilioClient
is more reliable and performant.
HttpClient
objects created by the HTTP client factory are also configured to log HTTP requests. You can control the log level via configuration using the following log categories: System.Net.Http.HttpClient.Twilio.LogicalHandler
and System.Net.Http.HttpClient.Twilio.ClientHandler
.
Here's how you set the log level to Information
using appsettings.json:
{
"Logging": {
"LogLevel": {
...,
"System.Net.Http.HttpClient.Twilio.LogicalHandler": "Information",
"System.Net.Http.HttpClient.Twilio.ClientHandler": "Information"
}
},
...
}
If for some reason you do need to change the way the HttpClient
is supplied to TwilioRestClient
, you can pass in a callback method to .AddTwilioClient
. You need this if you want to use a forward proxy.
Here's an example that creates a new HttpClient
for every instance of TwilioRestClient
that is requested using DI:
using Twilio.AspNet.Core;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTwilioClient(provider => new HttpClient())
The above code is for the sake of demonstration and could lead to port exhaustion issues again. When you supply your own HttpClient
, we still recommend you follow the best practices by using the HTTP client factory APIs.
Validate HTTP requests coming from Twilio
Your web application has to be publicly available for Twilio to send webhook requests to it. However, this means that anyone can send HTTP requests to your application which could be exploited. Luckily, you can secure your webhooks by validating the X-Twilio-Signature
HTTP header.
To make request validation easier and more closely integrated with MVC, Twilio.AspNet provides the [ValidateRequest]
attribute. Before v6.0.0, you could apply the attribute in places like controllers and actions like this:
using Microsoft.AspNetCore.Mvc;
using Twilio.AspNet.Core;
public class SmsController : Controller
{
// ⚠️ old way of validating from before pre-v6.0.0, do not use
[ValidateRequest("[YOUR_AUTH_TOKEN]", urlOverride: "https://??????.ngrok.io/sms")]
public void Index() {}
}
Now any requests made to the SmsController.Index
action would first be validated by the ValidateRequest
attribute and if the request did not originate from Twilio, an HTTP 403 Forbidden status code would be responded with.
This worked great, but you had to configure the request validation in code, which introduced some challenges:
- Hard-coding the auth token into code meant that this sensitive secret would be embedded in the DLL-files and also tracked in source control systems, both of which increased the chance of leaking the auth token.
- Depending on where you're deploying your application (dev/staging/production environments), your configuration would be hard-coded and could not be changed. Each environment may require a different configuration, but there's no easy way to do that.
To resolve these issues, we had to remove the constructor parameters and properties from the ValidateRequest
attribute and use external configuration to configure request validation.
Going forward from v6.0.0, use the ValidateRequest
attribute without any parameters.
using Microsoft.AspNetCore.Mvc;
using Twilio.AspNet.Core;
public class SmsController : Controller
{
[ValidateRequest]
public void Index() {}
}
Next, you'll need to configure request validation, but this is done differently depending on whether you're using ASP.NET Core or ASP.NET MVC on .NET Framework.
On ASP.NET Core, add .AddTwilioRequestValidation()
to your startup.
using Twilio.AspNet.Core;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTwilioRequestValidation();
Then use .NET configuration to configure the request validation. Here's the JSON representation of the configuration:
{
"Twilio": {
"RequestValidation": {
"AuthToken": "[YOUR_AUTH_TOKEN]",
"AllowLocal": true,
"BaseUrlOverride": "https://??????.ngrok.io"
}
}
}
A couple of notes about the configuration:
- You can configure
Twilio:AuthToken
whichTwilio:RequestValidation:AuthToken
will fall back to. AllowLocal
will skip validation when the HTTP request originated from localhost.- Use
BaseUrlOverride
in case your app is behind a reverse proxy or a tunnel like ngrok. The path of the current request will be appended to theBaseUrlOverride
for request validation.
While the example above shows the JSON representation of the configuration, you can use any configuration provider plugged into your application. The AuthToken
in particular should not be stored in your appsettings.json file. Instead, use user-secrets, a vault service, or environment variables instead.
You can also configure request validation using code:
builder.Services
.AddTwilioRequestValidation((serviceProvider, options) =>
{
options.AuthToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");
options.AllowLocal = true;
options.BaseUrlOverride = "https://??????.ngrok.io";
});
Do not hard-code your Auth Token into code and do not check them into source control. We recommend using the Secrets Manager for local development. Alternatively, you can use environment variables, a vault service, or other secure techniques.
That's it! Now you can use external configuration like JSON files, environment variables, user-secrets, etc. to configure request validation. This makes your application easier to deploy to other environments.
For ASP.NET MVC on .NET Framework, you can configure request validation using the Web.config file using the twilio/requestValidation
configuration element:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="twilio" type="Twilio.AspNet.Mvc.TwilioSectionGroup,Twilio.AspNet.Mvc">
<section name="requestValidation" type="Twilio.AspNet.Mvc.RequestValidationConfigurationSection,Twilio.AspNet.Mvc"/>
</sectionGroup>
</configSections>
<twilio>
<requestValidation
authToken="[YOUR_AUTH_TOKEN]"
baseUrlOverride="https://??????.ngrok.io"
allowLocal="true"
/>
</twilio>
</configuration>
Alternatively, you can configure request validation using app settings:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="twilio:requestValidation:authToken" value="[YOUR_AUTH_TOKEN]"/>
<add key="twilio:requestValidation:baseUrlOverride" value="https://??????.ngrok.io"/>
<add key="twilio:requestValidation:allowLocal" value="true"/>
</appSettings>
</configuration>
Now that request validation is configured, you can apply the ValidateRequest
attribute to your MVC apps globally, regions, controllers, and actions to protect your endpoints.
Go use the shiny new bits
You can take advantage of these new features and enhancements now by installing the latest version of the Twilio helper library for ASP.NET. You can find the installation instructions in the readme of the Twilio.AspNet GitHub repository. If you like this library, consider giving it a star on the GitHub repo. Also you are welcome to submit an issue if you run into problems.
We can't wait to see what you'll build with Twilio.AspNet. Let us know on social media and don't forget to mention @TwilioDevs and @RealSwimburger on Twitter or LinkedIn.
Niels Swimberghe is a Belgian American software engineer and technical content creator at Twilio. Get in touch with Niels on Twitter @RealSwimburger and follow Niels’ personal blog on .NET, Azure, and web development at swimburger.net.
Related Posts
Recommend
-
28
Interacting with APIs usually requires a lot of networking code. You can solve this problem by abstracting all of that and calling Swift functions to get the results. In this tutorial you’ll learn how to create an API help...
-
6
The new Twilio Console is now in Public Beta. Access the right products and tools at the right time. Enjoy vastly improved page load speed as a result of a re-architecture. Find dedicated views for your products,...
-
12
twilio-php The default branch name for this repository has been changed to main as of 07/27/2020. Documentation The documentation for the Twilio API can be found
-
5
How can I use the twilio-php library to retrieve all calls made between two dates? advertisements Using the following code I am able to get th...
-
4
April 6, 2021 paru 是什么?⚓ 根据
-
5
Download library source and cookbook examples - 12.7 KB
-
7
Introducing Twilio's Go Helper Library
-
8
TechTwilio shares plunge 34% on light guidancePublished Thu, N...
-
6
DiscountsCalculator Helper library for calculating discounts in a application. Installation This library are developed to PHP 8.1, use Enum so it's not possible using in previously versions, if you like you can put a PR to...
-
5
Ranked #18 for today...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK