7
\$\begingroup\$

So, I have an ASP.NET Core 3.1 web site that makes occasional calls to a vendor's RESTful API. I use the HttpClient class to perform these calls.

Currently, the web site is relatively low traffic (probably a couple of users per day). The current primary use case will result in 3 calls to the API for the complete workflow.

Even in previous versions of the framework, Microsoft recommended just creating a single instance of HttpClient and using it throughout the entire lifecycle of your application (to prevent port exhaustion). The new recommendation for ASP.NET Core is to register a service in your Startup class to act as a factory for your HttpClient. I initially used my own Singleton for but recently switched to follow Microsoft's recommendation.

My one concern is that I have to add my API Key as a header for each request, or it will be rejected.

I currently store the database key in SQL Server, using its always-encrypted feature to make sure that it stays private.

Here's the relevant logic in the ConfigureServices method in my Startup class:

services.AddDbContext<DatabaseContext>();

// Use TLS 1.3 for requests
// I'm a little uncertain if this is still the recommended way to configure this in ASP.NET Core
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13;

services.AddHttpClient("VendorAPICall", c =>
        {
            c.BaseAddress = new Uri("https://api.vendorname.com/");

            c.Timeout = new TimeSpan(4, 0, 0);

            c.DefaultRequestHeaders.Accept.Clear();

            c.DefaultRequestHeaders.Add("Accept", "application/json");

            // I store the API Key securely using SQL Server's Always Encrypted feature: https://docs.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-database-engine?view=sql-server-ver15
            // I'm a little concerned about the potential performance impact of this, especially how
            // many extra database calls it could result in (particularly since I'm not doing
            // the async calls).
            using (var ctx = new DatabaseContext())
            {
                Security security = ctx.Security.First();

                // Header must include the API Token for all requests, or the request will be rejected 
                c.DefaultRequestHeaders.Add("ApiToken", security.Apikey);
            }
        });

I have two points that I'm concerned about:

First, is the call to ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13; still the recommended (or, at least, acceptable) way of doing this configuration in ASP.NET Core, or am I doing this wrong?

Secondly, I'm concerned about the database call and how many back-and-forths this could result in for the database. This seems a little unnecessary, since the API Key will only be renewed every 6 months or so, so it seems wasteful at a minimum and a potential scalability issue at worst.

I'm also a little annoyed that I'm not using the async version of the Entity Framework calls here. (I'm otherwise using the async version of all of the database calls wherever possible).

Am I worrying too much here, or is there actually a better way to do this? (I already established in a separate Q&A on Stack Overflow that I was, in fact, being paranoid about the possibility of keeping the API Key in memory, but that's probably a separate issue).

\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

No need to manually create the DbContext. Since you have already registered it with the service collection, resolve it as needed.

AddHttpClient has an overload that provides access to the service provider.

services.AddHttpClient("VendorAPICall", (sp, c) => {
    c.BaseAddress = new Uri("https://api.vendorname.com/");
    c.Timeout = new TimeSpan(4, 0, 0);
    c.DefaultRequestHeaders.Accept.Clear();
    c.DefaultRequestHeaders.Add("Accept", "application/json");
    var ctx = sp.GetService<DatabaseContext>(); //<-- resolve context
    Security security = ctx.Security.First();
    // Header must include the API Token for all requests, 
    // or the request will be rejected 
    c.DefaultRequestHeaders.Add("ApiToken", security.Apikey);

});

This will allow the framework to manage its lifetime instead of constantly having to dispose of it yourself in a using block

\$\endgroup\$
3
  • \$\begingroup\$ Thanks, seems like that alone will save resources. Once I have that, I can assume that the db call itself isn't all that terrible of overhead? \$\endgroup\$ Apr 17, 2020 at 21:41
  • \$\begingroup\$ Congrats on getting close vote rights on this site too BTW :) \$\endgroup\$ Apr 17, 2020 at 21:44
  • \$\begingroup\$ @EJoshuaS-ReinstateMonica Glad to help. \$\endgroup\$
    – Nkosi
    Apr 17, 2020 at 21:48
4
\$\begingroup\$

Let me reflect to your other question, which is related to the good old ServicePointManager.

In the early versions of .NET Core they tried to get rid of the ServicePointManager and the related HttpWebRequest class as well. Instead they have introduced two new classes: WinHttpHandler and CurlHandler. Former should be used in Windows environment and latter should be used everywhere else.

So prior .NET Core 2.0, you had to write something like this:

var httpHandler = new WinHttpHandler();
httpHandler.SslProtocols = SslProtocols.Tls13;
var httpClient = new HttpClient(httpHandler);  

But in .NET Core 2.0 it was reintroduced but in a slightly different way. Please read this excellent article if you are interested about the details.

So, in short: Yes, you can still use in .NET Core 3 the ServicePointManager, which now resides inside the System.Net.ServicePoint.dll.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.