The codebase I work with is mixed with greenfield and legacy projects. Some projects use a static HttpClient (.NET Framework) to avoid socket exceptions and others use Flurl. For newer projects we use a mix of Flurl and HttpClientFactory (via typed and named clients).
The team is looking to standardize on Flurl or HttpClientFactory. We're leaving Refit out of consideration at this point.
Flurl
Flurl is a modern, fluent, asynchronous, testable, portable, URL builder and HTTP client library for .NET.
Pros
The fluent builder interface is easy to work with. Very easy to get started without knowledge of HttpClient, HttpRequestMessage, HttpResponseMessage or serializers.
The classes that use Flurl can be easily unit testable with HttpTest(). In the past we've had to use mockhttp for mocking the HttpClient
There is still a lot of customizations that can be done to Flurl via creating your own HttpClientFactory (by inheriting the DefaultHttpClientFactory specific to Flurl). They would apply via FlurlHttp.GlobalSettings (static), IFlurlClient.Settings or IFlurlRequest.Settings.
There are global settings and delegates that can be used to handle the requests, responses and errors through OnBeforeCallAsync, OnAfterCallAsync and OnErrorAsync. They could be specified via IFlurlClient.Settings or IFlurlRequest.Settings if global configuration isn't warranted.
Cons
An update to the package may require updates to code especially if it is a major version change (2.8.2 to 3.0.1).
Lots of wrappers for a fluent interface around HttpClient which adds overhead, but should not add much time to the transactions in relation to the actual HTTP call.
Resiliency policies through Polly are expected to be provided through the constructor of classes that consume Flurl so that they can wrap around the Flurl code. There may be a better way of doing this with FlurlClient?
Use of System.Text.Json is not supported natively and requires custom extensions at the moment Issue 517.
Some static analysis tools like Snyk\Veracode can report violations with Flurl.
As flexible as Flurl is, there may be current configurations that aren't possible. New features/configurations to .NET HttpClient or HttpClientFactory may take time to make it into a new version.
Neutral
- Open source so bug fixes are dependent on the community or you.
HttpClientFactory
The standard and recommended option for resolving an HttpClient. The client is created new or resolved each time depending on use of HttpClientFactory.Create() or use of a TypedClient, but the HttpMessageHandler is reused from a pool. TypedClients are resolved from HttpClientFactory through DI for consuming classes.
Pros
Updates are unlikely to break existing functionality or require updates to code.
The HttpClientFactory is configured at the top layer and added to the .NET DI ServiceCollection.
Top level DI configuration of resiliency policies through Polly extension. This is cleaner than injecting the resilience policies through each constructor that requires it for HTTP calls.
The other side of the coin for flexibility and numerous ways of conducting the HTTP transactions: Ability to customize HttpRequestHandlers to serializer. This can improve performance in the case of SocketsHttpHandler to deserializing directly from stream to object (instead of stream to string to object). Passing in options HttpClient methods like HttpCompletionOption.ResponseHeadersRead to increase performance for specific situations.
Supported by Microsoft.
Cons
There are many ways in which HTTP calls could be conducted. Via the HttpClient directly, creation of HttpRequestMessage.
More code is required in comparison to Flurl in order to make requests and process the response.
More code is also required in order to mock HttpClient functionality either through mockhttp](github.com/richardszalay/mockhttp) or [Moq] (github.com/moq/moq4). It does give you more flexibility, but still cumbersome for unit testing.
Neutral
Use of TypedClients are added as Transient scope in the DI container which could be an impact to total memory footprint vs adding an HttpClientFactory to the container as Singleton scope and resolving the HttpClient. If there are multiple clients that hit different endpoints, the BaseUri can be updated within the consuming class or named clients could be used.
Source is available so one can understand what happens underneath the covers, but not consolidated like Flurl. Contribution back to source may not be as easy as Flurl contributions.
Summary
My preference is to use Flurl for any end-to-end (test clients that hit the API under test) and use a standard pattern for DI injection and usage of HttpClientFactory created clients. Using Flurl for anything that doesn't require very high performance requirements. Once the client is written, it's often times not changed which is why I would prefer use of the HttpClientFactory in general even if it takes longer to develop for, the code is written it can be abstracted for use in the general case.
For clients, unit test coverage is important, but I find that integration tests are important for situations where we want to make sure that the endpoint behaves as expected which can aid in troubleshooting.
Did I miss anything?
References
- flurl.dev
- docs.microsoft.com/en-us/aspnet/core/fundam..
- github.com/reactiveui/refit
- docs.microsoft.com/en-us/dotnet/api/system...
- stevejgordon.co.uk/using-httpcompletionopti..
- github.com/richardszalay/mockhttp
- aspnetmonsters.com/2016/08/2016-08-27-httpc..
- josef.codes/you-are-probably-still-using-ht..