How should you build an OData URI?

2k Views Asked by At

I'm looking to build URIs such as https://example.com/data/customers?$top=100.

Is there a UriBuilder for creating OData URIs (i.e. which can handle characters such as $ appropriately)?

Full info

I have code like this (simplified example):

public Uri CreateMyApiUri(string rootUri, string apiPath, string entity, int pageSize)
{
    var builder = new UriBuilder(rootUri);
    builder.Path = ConcatPathParts(builder.Path, apiPath, entity); //basically string.Join("/", args), plus code to remove superfluous slashes
    var parameters = HttpUtility.ParseQueryString(builder.Query);
    if (pageSize > 0) parameters["$top"] = pageSize.ToString();
    builder.Query = parameters.ToString();
    return builder.Uri; 
}
//called like this
var uri = CreateMyApiUri("https://example.com", "data", "customers", 100);

However, the OData special character $ gets encoded for use in the URI as %24.

I've found OData.Net on GitHub which seems a helpful library for such things, but it's not part of the standard library and looks quite heavyweight for my simple need, so I'm hoping to find something simpler before committing to that going down the OData.Net path...

Of course, I could avoid this by doing a simple var uri = string.Join("/", new [] {rootUri, apiPath, entity, $"?$top={pageSize}"});... but I want to ensure I'm taking advantage of the .net library's character escaping features / not creating a solution for something the framework already gives me.

NB: I'm aware that you can generate classes from OData services, but I don't want to use this approach since that requires that I regenerate the client code if the API is changed (e.g. new fields are added to the target entity). Instead I want to use a more "pure" HTTP approach.

1

There are 1 best solutions below

0
On BEST ANSWER

I found a solution; I didn't need a special ODataUriBuilder; rather there was a bug in my use of query.ToString(), as explained here: https://stackoverflow.com/a/26789977/361842

Applying that fix to the above code solves the issue:

public Uri CreateMyApiUri(string rootUri, string apiPath, string entity, int pageSize)
{
    var builder = new UriBuilder(rootUri);
    builder.Path = ConcatPathParts(builder.Path, apiPath, entity); //basically string.Join("/", args), plus code to remove superfluous slashes
    var parameters = HttpUtility.ParseQueryString(builder.Query);
    if (pageSize > 0) parameters["$top"] = pageSize.ToString();

    //the fix:
    builder.Query = Uri.EscapeUriString(HttpUtility.UrlDecode(parameters.ToString()));
    //instead of:
    //builder.Query = parameters.ToString();

    return builder.Uri; 
}
//called like this
var uri = CreateMyApiUri("https://example.com", "data", "customers", 100);