Runtime: UriTemplate from System.ServiceModel

Created on 15 Oct 2017  路  21Comments  路  Source: dotnet/runtime

It would be nice to be able to do something like this:

private static readonly UriTemplate _activationTemplate = new UriTemplate("license/activate/{licenseKey}");
public async Task<LicenseActivationResponse> ActivateAsync(LicenseActivationRequest activationRequest)
{
    var requestUrl = _activationTemplate.BindByPosition(_httpClient.BaseAddress, activationRequest.LicenseKey);
    var serialized = JsonConvert.SerializeObject(activationRequest, _serializerSettings);
    var content = new StringContent(serialized, Encoding.UTF8, "application/json");

    using (var response = await _httpClient.PutAsync(requestUrl, content).ConfigureAwait(false))
    {
        // ...
    }
    // ...
}

dotnet/runtime#17085 asks about the same thing, and the response was:

Thank you for the suggestion. It is a good idea in general about having templates, but we think it belongs where it is in WCF and is not generally applicable to .NET Core.

I think that comment misunderstands the use case for UriTemplates. There's nothing WCF-specific about UriTemplate. In fact, the code above is taken from a service client implementation that interacts with an ASP.NET Core web service.

UriTemplate is used to safely build URLs. It's safer, and more capable than string concatenation. Building URLs isn't specific to WCF, so it would be nice if it were available in .NET Core.

api-needs-work area-System.Runtime

Most helpful comment

Missing slashes or double slashes is a common gotcha. Optional query parameters are also things people struggle with. Multi-segment paths and optional paths are easy with a template. Another nice side benefit is query parameters always appear in the same order which can be helpful.

It would be great to see this feature in ASPNetCore or even better in System.Net.Http because people don't want to pull in a 3rd party dependency for what they initially see as just string concatenation. It's once you start using templates regularly that you start to realize all the ways it saves you from pain.

Consider chunks of code like this that are _all over_ the Azure SDKs,

c# var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ApiManagement/service/{serviceName}/apis/{apiId}").ToString(); _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(resourceGroupName)); _url = _url.Replace("{serviceName}", System.Uri.EscapeDataString(serviceName)); _url = _url.Replace("{apiId}", System.Uri.EscapeDataString(apiId)); _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); List<string> _queryParameters = new List<string>(); if (format != null) { _queryParameters.Add(string.Format("format={0}", System.Uri.EscapeDataString(format))); } if (export != null) { _queryParameters.Add(string.Format("export={0}", System.Uri.EscapeDataString(export))); } if (Client.ApiVersion != null) { _queryParameters.Add(string.Format("api-version={0}", System.Uri.EscapeDataString(Client.ApiVersion))); } if (_queryParameters.Count > 0) { _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); }

https://github.com/Azure/azure-sdk-for-net/blob/61f82ba1f4d7a4e5ef31a7480768f514248f49aa/src/SDKs/ApiManagement/Management.ApiManagement/Generated/ApiExportOperations.cs#L167-L188

This can be replaced by a single line using a UrlTemplate

All 21 comments

馃挱 https://github.com/tunnelvisionlabs/dotnet-uritemplate

I don't think I ever released a stable version (lack of external interest), but it could probably be done.

For lack of a first-party solution, I would use the heck out of a nuget replacement. Another person linked their nuget package in dotnet/runtime#17085 that purported to be a replacement, but it didn't have BindByName or BindByPosition, wihch I use extensively.

@rianjs The Tavis.UriTemplates supports bind by name. I don't think bind by position would be too hard to implement.
The System.ServiceModel UriTemplate library does a better job of parsing URLs for inbound requests.

@rianjs do you mind writing up a formal api proposal along with the usecases where you think this might be useful?

@rianjs Is the missing bindByPosition the only thing stopping you from using Tavis.UriTemplates ? Might be enough incentive to get me to do the .net Standard 2.0 port and add strong naming while I'm at it :-)

@joperezr I show a bunch of common cases for why you want to use a library to do URL construction here http://www.bizcoder.com/constructing-urls-the-easy-way

@darrelmiller another missing functionality preventing me from using Tavis.UriTemplates is lack of support for wildcards - which I know are not part of rfc6570 - but were supported by wcf Uri template.

c# var template = new UriTemplate("http://localhost/{*x}/", false) var values = template.GetParameters("http://localhost/foo/1/"); // values is null
Any suggestions on how to work around this?

@hnafar The UriTemplate equivalent of the * wildcard is http://localhost{/x}/ I'm not sure if the current parameter parsing handles this scenario, but if it doesn't then yes we should try and fix that.

It seems that UriTemplate is not in .NET standard either, not in System.ServiceModel.Primitives.

UriTemplate is really helpful for writing dev tool. Just wonder if it will be available in .net core 2.3?

@darrelmiller @davidsh would it be possible to add some form of RFC6570 (URITemplate) support to Microsoft.AspNetCore.Http.Extensions ? It would seem like a natural fit alongside QueryBuilder which lives there. I was really surprised that there wasn't a package to do this currently in .NET Core. Thanks! I don't know if it would be realistic to adopt Tavis.UriTemplates as an option?

cc: @rmkerr

@darrelmiller @davidsh would it be possible to add some form of RFC6570 (URITemplate) support to Microsoft.AspNetCore.Http.Extensions

@Tratcher Can you comment on this feature request for Microsoft.AspNetCore.Http.Extensions?

UriTemplate is used to safely build URLs. It's safer, and more capable than string concatenation.

How so? Because the template handles the character escaping when inserting the values?

@davidsh Microsoft.AspNetCore.Http.Extensions is one place this could be provided, but by no means the only place. The examples given above already show this is useful for client apps using HttpClient.

@davidsh Microsoft.AspNetCore.Http.Extensions is one place this could be provided, but by no means the only place. The examples given above already show this is useful for client apps using HttpClient.

@Tratcher, the only reason that I mentioned Http.Extensions as a possible landing place is that the original issue had the following comment from @davidsh, but now that there's Http.Extensions I thought I would bring up the request again as maybe now there's an appropriate place for it whereas there wasn't necessarily before?:

Thank you for the suggestion. It is a good idea in general about having templates, but we think it belongs where it is in WCF and is not generally applicable to .NET Core.

Here's some motivation background from RFC 6570. For me, it just reduces everyone writing 'reinventing the wheel (poorly)' utility classes, ensures consistent correct behavior among various teams, and means that I don't have to worry about things like missing/doubled slashes when concating fragments, escaping, etc. Does that help?

URI Templates provide a mechanism for abstracting a space of resource
identifiers such that the variable parts can be easily identified and
described. URI Templates can have many uses, including the discovery
of available services, configuring resource mappings, defining
computed links, specifying interfaces, and other forms of
programmatic interaction with resources.

Missing slashes or double slashes is a common gotcha. Optional query parameters are also things people struggle with. Multi-segment paths and optional paths are easy with a template. Another nice side benefit is query parameters always appear in the same order which can be helpful.

It would be great to see this feature in ASPNetCore or even better in System.Net.Http because people don't want to pull in a 3rd party dependency for what they initially see as just string concatenation. It's once you start using templates regularly that you start to realize all the ways it saves you from pain.

Consider chunks of code like this that are _all over_ the Azure SDKs,

c# var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ApiManagement/service/{serviceName}/apis/{apiId}").ToString(); _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(resourceGroupName)); _url = _url.Replace("{serviceName}", System.Uri.EscapeDataString(serviceName)); _url = _url.Replace("{apiId}", System.Uri.EscapeDataString(apiId)); _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); List<string> _queryParameters = new List<string>(); if (format != null) { _queryParameters.Add(string.Format("format={0}", System.Uri.EscapeDataString(format))); } if (export != null) { _queryParameters.Add(string.Format("export={0}", System.Uri.EscapeDataString(export))); } if (Client.ApiVersion != null) { _queryParameters.Add(string.Format("api-version={0}", System.Uri.EscapeDataString(Client.ApiVersion))); } if (_queryParameters.Count > 0) { _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); }

https://github.com/Azure/azure-sdk-for-net/blob/61f82ba1f4d7a4e5ef31a7480768f514248f49aa/src/SDKs/ApiManagement/Management.ApiManagement/Generated/ApiExportOperations.cs#L167-L188

This can be replaced by a single line using a UrlTemplate

@darrelmiller that's exactly the kind of thing we see too, it's one of those things that seems 'easy' to start with string concat but then as requirements (and/or code duplication) grows everyone either rolls their own utility lib or pulls in a dep :)

@Tratcher , who would be a good PM to loop in and see if it would be possible to work this into the ASP.Net Core 3.0 timeline? THanks!

@shirhatti as he covers both the .NET Core and AspNetCore sides.

Just to confirm, is this feature on the road map for netcore 3.x? If so, can you perhaps link it here?
Thank you

Not for 3.x that I'm aware of. 3.1 has shipped and so we are only shipping servicing fixes, which will very rarely include API additions. According to the Milestone on the issue, this is currently scoped as part of 5.0

Thanks for the update @joperezr

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bencz picture bencz  路  3Comments

noahfalk picture noahfalk  路  3Comments

GitAntoinee picture GitAntoinee  路  3Comments

jkotas picture jkotas  路  3Comments

nalywa picture nalywa  路  3Comments