Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Uri strings that may or may not have trailing /'s

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?

like image 284
Garrison Neely Avatar asked Oct 31 '22 13:10

Garrison Neely


1 Answers

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>();
like image 67
Todd Menier Avatar answered Nov 15 '22 06:11

Todd Menier