dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.27k stars 4.73k forks source link

UriTemplate from System.ServiceModel #23848

Open rianjs opened 7 years ago

rianjs commented 7 years ago

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.

sharwell commented 7 years ago

💭 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.

rianjs commented 7 years ago

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 commented 7 years ago

Here's the original source, actually: http://referencesource.microsoft.com/#System.ServiceModel/System/UriTemplate.cs

darrelmiller commented 7 years ago

@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.

joperezr commented 6 years ago

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

darrelmiller commented 6 years ago

@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

hnafar commented 6 years ago

@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.

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?

darrelmiller commented 6 years ago

@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.

zijianhuang commented 6 years ago

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?

ericsampson commented 5 years ago

@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?

davidsh commented 5 years ago

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?

Tratcher commented 5 years ago

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.

ericsampson commented 5 years ago

@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.

ericsampson commented 5 years ago

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.

darrelmiller commented 5 years ago

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,

            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

ericsampson commented 5 years ago

@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 :)

ericsampson commented 5 years ago

@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!

Tratcher commented 5 years ago

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

devzaak commented 4 years ago

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

joperezr commented 4 years ago

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

devzaak commented 4 years ago

Thanks for the update @joperezr

gregsdennis commented 1 year ago

It's now mid-2023, and .Net 8 is almost out. Is there any hope of UriTemplate being included?

hrobertson commented 10 months ago

@dotnet/ncl is this something that could be reviewed?

An implementation of support for RFC 6570 URI templates seems like a good fit for the standard library along side System.Uri, System.UriBuilder, etc.

It doesn't seem to me like something that should be in dotnet/aspnetcore as it's not specific to hosting. Making web requests is not the exclusive domain of web applications!

I don't know about any policy regarding adding functionality to dotnet that is already (mostly) provided by third-party libraries?