How should you build an OData URI?
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.
c# odata query-string querystringparameter uribuilder
add a comment |
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.
c# odata query-string querystringparameter uribuilder
OData publishes its schema. If the API changes, the schema will also change. That's no less "pure HTTP" than any other approach. In fact, GraphQL and Open API try to bring back that discoverable schema
– Panagiotis Kanavos
Nov 22 '18 at 9:26
@PanagiotisKanavos by "non pure HTTP" I mean where you have a C# client class, such asProductClient
in docs.microsoft.com/en-us/aspnet/web-api/overview/…. I'm writing middleware which picks up the data and converts it to XML, which can then be transformed via XLSTs before being consumed by other systems. By avoiding generated C# I don't have to redeploy assemblies; I can update the middleware using only configuration (XSLT / URI) for the affected service. There may be a better way though / I'm open to ideas.
– JohnLBevan
Nov 22 '18 at 10:09
1
that's not a matter of pureness, it's a matter of versioning that won't be solved by using XSLT or JSON Path - you need to know the schema to write the proper transformation. The ODATA client allows you to write LINQ queries, retrieve only the fields you want and map them to whatever client entity you want, or even use anonymous types. There's no real difference between an XSLT mapping and a LINQ query, except for ease of use, compile-time checking etc
– Panagiotis Kanavos
Nov 22 '18 at 10:53
add a comment |
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.
c# odata query-string querystringparameter uribuilder
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.
c# odata query-string querystringparameter uribuilder
c# odata query-string querystringparameter uribuilder
edited Nov 22 '18 at 9:10
JohnLBevan
asked Nov 21 '18 at 18:55
JohnLBevanJohnLBevan
14.5k146108
14.5k146108
OData publishes its schema. If the API changes, the schema will also change. That's no less "pure HTTP" than any other approach. In fact, GraphQL and Open API try to bring back that discoverable schema
– Panagiotis Kanavos
Nov 22 '18 at 9:26
@PanagiotisKanavos by "non pure HTTP" I mean where you have a C# client class, such asProductClient
in docs.microsoft.com/en-us/aspnet/web-api/overview/…. I'm writing middleware which picks up the data and converts it to XML, which can then be transformed via XLSTs before being consumed by other systems. By avoiding generated C# I don't have to redeploy assemblies; I can update the middleware using only configuration (XSLT / URI) for the affected service. There may be a better way though / I'm open to ideas.
– JohnLBevan
Nov 22 '18 at 10:09
1
that's not a matter of pureness, it's a matter of versioning that won't be solved by using XSLT or JSON Path - you need to know the schema to write the proper transformation. The ODATA client allows you to write LINQ queries, retrieve only the fields you want and map them to whatever client entity you want, or even use anonymous types. There's no real difference between an XSLT mapping and a LINQ query, except for ease of use, compile-time checking etc
– Panagiotis Kanavos
Nov 22 '18 at 10:53
add a comment |
OData publishes its schema. If the API changes, the schema will also change. That's no less "pure HTTP" than any other approach. In fact, GraphQL and Open API try to bring back that discoverable schema
– Panagiotis Kanavos
Nov 22 '18 at 9:26
@PanagiotisKanavos by "non pure HTTP" I mean where you have a C# client class, such asProductClient
in docs.microsoft.com/en-us/aspnet/web-api/overview/…. I'm writing middleware which picks up the data and converts it to XML, which can then be transformed via XLSTs before being consumed by other systems. By avoiding generated C# I don't have to redeploy assemblies; I can update the middleware using only configuration (XSLT / URI) for the affected service. There may be a better way though / I'm open to ideas.
– JohnLBevan
Nov 22 '18 at 10:09
1
that's not a matter of pureness, it's a matter of versioning that won't be solved by using XSLT or JSON Path - you need to know the schema to write the proper transformation. The ODATA client allows you to write LINQ queries, retrieve only the fields you want and map them to whatever client entity you want, or even use anonymous types. There's no real difference between an XSLT mapping and a LINQ query, except for ease of use, compile-time checking etc
– Panagiotis Kanavos
Nov 22 '18 at 10:53
OData publishes its schema. If the API changes, the schema will also change. That's no less "pure HTTP" than any other approach. In fact, GraphQL and Open API try to bring back that discoverable schema
– Panagiotis Kanavos
Nov 22 '18 at 9:26
OData publishes its schema. If the API changes, the schema will also change. That's no less "pure HTTP" than any other approach. In fact, GraphQL and Open API try to bring back that discoverable schema
– Panagiotis Kanavos
Nov 22 '18 at 9:26
@PanagiotisKanavos by "non pure HTTP" I mean where you have a C# client class, such as
ProductClient
in docs.microsoft.com/en-us/aspnet/web-api/overview/…. I'm writing middleware which picks up the data and converts it to XML, which can then be transformed via XLSTs before being consumed by other systems. By avoiding generated C# I don't have to redeploy assemblies; I can update the middleware using only configuration (XSLT / URI) for the affected service. There may be a better way though / I'm open to ideas.– JohnLBevan
Nov 22 '18 at 10:09
@PanagiotisKanavos by "non pure HTTP" I mean where you have a C# client class, such as
ProductClient
in docs.microsoft.com/en-us/aspnet/web-api/overview/…. I'm writing middleware which picks up the data and converts it to XML, which can then be transformed via XLSTs before being consumed by other systems. By avoiding generated C# I don't have to redeploy assemblies; I can update the middleware using only configuration (XSLT / URI) for the affected service. There may be a better way though / I'm open to ideas.– JohnLBevan
Nov 22 '18 at 10:09
1
1
that's not a matter of pureness, it's a matter of versioning that won't be solved by using XSLT or JSON Path - you need to know the schema to write the proper transformation. The ODATA client allows you to write LINQ queries, retrieve only the fields you want and map them to whatever client entity you want, or even use anonymous types. There's no real difference between an XSLT mapping and a LINQ query, except for ease of use, compile-time checking etc
– Panagiotis Kanavos
Nov 22 '18 at 10:53
that's not a matter of pureness, it's a matter of versioning that won't be solved by using XSLT or JSON Path - you need to know the schema to write the proper transformation. The ODATA client allows you to write LINQ queries, retrieve only the fields you want and map them to whatever client entity you want, or even use anonymous types. There's no real difference between an XSLT mapping and a LINQ query, except for ease of use, compile-time checking etc
– Panagiotis Kanavos
Nov 22 '18 at 10:53
add a comment |
1 Answer
1
active
oldest
votes
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);
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53418841%2fhow-should-you-build-an-odata-uri%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
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);
add a comment |
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);
add a comment |
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);
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);
answered Nov 22 '18 at 9:18
JohnLBevanJohnLBevan
14.5k146108
14.5k146108
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53418841%2fhow-should-you-build-an-odata-uri%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
OData publishes its schema. If the API changes, the schema will also change. That's no less "pure HTTP" than any other approach. In fact, GraphQL and Open API try to bring back that discoverable schema
– Panagiotis Kanavos
Nov 22 '18 at 9:26
@PanagiotisKanavos by "non pure HTTP" I mean where you have a C# client class, such as
ProductClient
in docs.microsoft.com/en-us/aspnet/web-api/overview/…. I'm writing middleware which picks up the data and converts it to XML, which can then be transformed via XLSTs before being consumed by other systems. By avoiding generated C# I don't have to redeploy assemblies; I can update the middleware using only configuration (XSLT / URI) for the affected service. There may be a better way though / I'm open to ideas.– JohnLBevan
Nov 22 '18 at 10:09
1
that's not a matter of pureness, it's a matter of versioning that won't be solved by using XSLT or JSON Path - you need to know the schema to write the proper transformation. The ODATA client allows you to write LINQ queries, retrieve only the fields you want and map them to whatever client entity you want, or even use anonymous types. There's no real difference between an XSLT mapping and a LINQ query, except for ease of use, compile-time checking etc
– Panagiotis Kanavos
Nov 22 '18 at 10:53