IHttpClientFactory
From version 3.3.0 you can use .NET Core’s 2.1 IHttpClientFactory to create and manage the lifetime of the RestClient and the underline HttpClient.
After reading the Disposing section you may be inclined to think that scoping the RestClient as Singleton would be the best way to go, and for the most part you would be right. You would avoid the dreaded socket exhaustion issue, however you might get an issue with DNS updates which is a real issue if you are load balancing or have cloud managed services.
Knowing this limitation and realising that HttpClient’s are being used more from servers because of microservices, the .NET Core team have come up with IHttpClientFactory.
Before reading further if you don’t know about IHttpClientFactory, read @stevejgordon excellent blog post on IHttpClientFactory.
RestClient support for IHttpClientFactory is very similar to .NET Core’s so you should feel at home using it if you’ve used .NET Core’s IHttpClientFactory. The extra guidance I would give is set the handlerLifetime value higher if your Network configuration would allow it, 10 minutes is my go to. The default is safe but a shorter lived pool will come with a performance penalty.
To use IHttpClientFactory with RestClient first register your RestClient as a service in Startup.cs. This is the simplest version if you only need one RestClient.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRestClient("https://api.github.com");
}
}
Then inject IRestClientFactory
into your controller, which is how we support IHttpClientFactory.
public class GitHubController : Controller
{
private readonly IRestClientFactory _restClientFactory;
public GitHubController(IRestClientFactory restClientFactory)
{
_restClientFactory = restClientFactory;
}
[Route("github/users/dalsoft"), HttpGet]
public async Task<List<Repository>> CreateClient()
{
dynamic restClient = _restClientFactory.CreateClient();
var repositories = await restClient.users.dalsoft.repos.Get();
return repositories;
}
}
If you need to support multiple RestClient’s use a named client.
First register your named RestClient as a service in Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRestClient("MyNamedGitHubClient", "https://api.github.com/orgs/");
}
}
Then inject IRestClientFactory
into your controller (which is how we support IHttpClientFactory), and call CreateClient passing the name of your client.
public class GitHubController : Controller
{
private readonly IRestClientFactory _restClientFactory;
public GitHubController(IRestClientFactory restClientFactory)
{
_restClientFactory = restClientFactory;
}
[Route("github/orgs/dotnet/repos"), HttpGet]
public async Task<List<Repository>> CreateClient()
{
dynamic restClient = _restClientFactory.CreateClient("MyNamedGitHubClient"); //Get Client by name
var repositories = await restClient.dotnet.repos.Get();
return repositories;
}
}
You can configure the HttpClient that the RestClient uses in the same way you would if you were using IHttpClientFactory, via the HttpClientBuilder
property.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRestClient("MyNamedGitHubClient", "https://api.github.com/orgs/")
.HttpClientBuilder.ConfigureHttpClient(client =>
{
client.Timeout = TimeSpan.FromMinutes(1);
});
}
}
You can also use HttpClientBuilder
to configure the HandlerLifetime, again in the same way you would if you were using IHttpClientFactory.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRestClient("MyNamedGitHubClient", "https://api.github.com/orgs/")
.HttpClientBuilder.SetHandlerLifetime(TimeSpan.FromMinutes(10));
}
}
Using HttpClientBuilder
you can also add Handlers directly to the HttpClient.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRestClient("MyNamedGitHubClient", "https://api.github.com/orgs/")
.HttpClientBuilder.AddHttpMessageHandler(() => new RetryHandler());
}
}
Although because you are using RestClient it is cleaner and easier to use the correct RestClient extension methods.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRestClient("MyNamedGitHubClient", "https://api.github.com/orgs/")
.UseRetryHandler();
}
}