I've been using the HttpClient in code for a while now and have always felt its use of Uris
has resulted in some brittleness in my implementation. Most of our service endpoint base addresses are in the app./web.config. As a result, they can be easily changed.
I've found that when using these endpoint strings to generate a Uri
, if they don't end in a /
, I get really wonky behavior. When calling GetAsync()
with a non-/
-terminated BaseAddress
, the resulting concatenated URL that is sent the GET request often drops either the string after the final /
in the BaseAddress
, or it will drop the string preceeding the first /
in the GetUri.
For example:
BaseAddress
: http://test/serviceEndpoint
GetUri
: api/customer
When HttpClient.GetAsync()
is called with that GetUri
, it will attempt to GET from http://test/api/customer
. If I cap BaseAddress
with a /
, everything works as expected.
My problem is that BaseAddress
is config-driven, and putting in a comment in the .config file saying "Make sure you end all Service URLs with a /
!" is a really brittle solution.
So I've gotten into the habit of using the following code in all of my HttpClient
construction:
var service = settings.GetValue("ServiceBaseUrl");
var serviceUri = !service.EndsWith("/")
? new Uri(service + "/")
: new Uri(service);
_client = new HttpClient
{
BaseAddress = serviceUri
};`
While this isn't brittle, it feels repetitive to have it in every HttpClient
constructor. Is there something in either HttpClient
or Uri
that I can use to avoid this boilerplate code?
There's nothing in HttpClient or Uri to address this, which is why I addressed it in a couple ways in Flurl. Flurl's AppendPathSegment
and AppendPathSegments
methods will ensure one and only one "/" separator between segments. For example, these yield the identical results:
"http://mysite/".AppendPathSegment("/endpoint")
"http://mysite".AppendPathSegment("endpoint")
The static Url.Combine
method also has this behavior, acting as sort of a Path.Combine
for URLs.
These and other helpful URL-building bits are available in the core Flurl package, but the real fun is in Flurl.Http, which combines the fluent URL builder with a lightweight wrapper on top of HttpClient and Json.NET that lets you go from string to URL to HTTP request to deserialized result without lifting pen from paper, so to speak:
var result = await settings.GetValue("ServiceBaseUrl")
.AppendPathSegment("endpoint")
.GetJsonAsync<T>();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With