Using ASP.NET Core 2.1's HttpClientFactory with Refit's REST library
When I moved my podcast site over to ASP.NET Core 2.1 I also started using HttpClientFactory and wrote up my experience. It's a nice clean way to centralize both settings and policy for your HttpClients, especially if you're using a lot of them to talk to a lot of small services.
Last year I explored Refit, an automatic type-safe REST library for .NET Standard. It makes it super easy to just declare the shape of a client and its associated REST API with a C# interface:
public interface IGitHubApi
{
[Get("/users/{user}")]
Task<User> GetUser(string user);
}
and then ask for an HttpClient that speaks that API's shape, then call it. Fabulous.
var gitHubApi = RestService.For<IGitHubApi>("https://api.github.com");
var octocat = await gitHubApi.GetUser("octocat");
But! What does Refit look like and how does it work in an HttpClientFactory-enabled world? Refit has recently been updated with first class support for ASP.NET Core 2.1's HttpClientFactory with the Refit.HttpClientFactory package.
Since you'll want to centralize all your HttpClient configuration in your ConfigureServices method in Startup, Refit adds a nice extension method hanging off of Services.
You add a RefitClient of a type, then add whatever other IHttpClientBuilder methods you want afterwards:
services.AddRefitClient<IWebApi>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"));
// Add additional IHttpClientBuilder chained methods as required here:
// .AddHttpMessageHandler<MyHandler>()
// .SetHandlerLifetime(TimeSpan.FromMinutes(2));
Of course, then you can just have your HttpClient automatically created and passed into the constructor. You'll see in this sample from their GitHub that you get an IWebAPI (that is, whatever type you want, like my IGitHubApi) and just go to town with a strongly typed interfaces of an HttpClient with autocomplete.
public class HomeController : Controller
{
public HomeController(IWebApi webApi)
{
_webApi = webApi;
}
private readonly IWebApi _webApi;
public async Task<IActionResult> Index(CancellationToken cancellationToken)
{
var thing = await _webApi.GetSomethingWeNeed(cancellationToken);
return View(thing);
}
}
Refit is easy to use, and even better with ASP.NET Core 2.1. Go get Refit and try it today!
* Strong image by Lucyb_22 used under Creative Commons from Flickr
Sponsor: Check out dotMemory Unit, a free unit testing framework for fighting all kinds of memory issues in your code. Extend your unit testing with the functionality of a memory profiler.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
Have you checked the security settings on your webcam? ;)
(He's darned good at it, isn't he?)
var person = await "https://api.com"
.AppendPathSegment("person")
.SetQueryParams(new { a = 1, b = 2 })
.WithOAuthBearerToken("my_oauth_token")
.PostJsonAsync(new
{
first_name = "Frank",
last_name = "Underwood"
})
.ReceiveJson<Person>();
So we're going to keep running into this... prepare to see more factory layers piled on for people who want to connect to multiple Redis caches, etc...
thanks for the nice article. I need to authenticate against the API and I know how it's done in general, but how to define the "AuthorizationHeaderValueGetter" in the Startup.cs? Within a RazorPage I cann get the token with:
var accessToken = await HttpContext.GetTokenAsync("access_token");but in the Startup.cs when configuring DI I do not hav a HttpContext yet:
services.AddRefitClient<IPolicyServerApi<Policy>>(new RefitSettings { AuthorizationHeaderValueGetter = () => Task.FromResult("TOKEN")}).ConfigureHttpClient(c => c.BaseAddress = new Uri("http://localhost:5001"));Does anyone have a hint for me? Thanks!
Comments are closed.
How is it that you ALWAYS end up having a blog post about something that's extremely beneficial to me? 🤔