0

I'm trying to consume a REST API using curly braces, but I can't figure out how to send them unescaped through HttpClient.GetFromJsonAsync.

string readKey = "1VQXp8053OlOs05XcoWX3L46TEQ4JaPnPtJMwUH6k0xUkgXWMd";
string endpoint = "https://api.cosmicjs.com/v2/buckets/ffm-blog-blog/objects?read_key={readKey}";
string queryOperator = "&query=";
string query = "%7Btype%3A%22posts%22%7D";
//Working URI (in browser and Postman) : https://api.cosmicjs.com/v2/buckets/ffm-blog-blog/objects?read_key=1VQXp8053OlOs05XcoWX3L46TEQ4JaPnPtJMwUH6k0xUkgXWMd&query=%7B%22type%22%3A%22posts%22%7D

I tried different approaches:

string uriA = endpoint + queryOperator + query;
string uriB = endpoint + queryOperator + Uri.UnescapedDataString(query);
Uri uriC = new Uri(endpoint + queryOperator + query);
Uri uriD = new Uri(endpoint + queryOperator + Uri.UnescapedDataString(query);
Uri uriE = new Uri(Uri.UnescapedDataString(endpoint + queryOperator + query);
...

Whatever method I try with HttpClient:

CosmicResponse cosmicReponse = await _http.GetFromJsonAsync<CosmicResponse>(uri);

I always get the same result:

System.Net.Http.HttpRequestException: TypeError: Failed to fetch

The URI sent by HttpClient always is :

https://api.cosmicjs.com/v2/buckets/ffm-blog-blog/objects?read_key=xxxx&query={%22type%22%3A%22posts%22}

and, of course, I doesn't work.

It works when sending a request without the query part:

string uri = "https://api.cosmicjs.com/v2/buckets/ffm-blog-blog/objects?read_key=xxxx";
CosmicResponse cosmicReponse = await _http.GetFromJsonAsync<CosmicResponse>(uri);

However, I need to use the query part.

I know using curly braces in an API is bad practice, but I didn't write the API and I'm just trying to consume it.

Would someone happen to know how to send "%7B" and "%7D" instead of "{" and "}" with HttpClient?

FULL CODE

public async Task<List<Post>> GetAllPosts()
{
    HttpClient http = new();
    string _rootUrl = "https://api.cosmicjs.com/v2/buckets/ffm-blog-blog";
    string _readKey = "1VQXp8053OlOs05XcoWX3L46TEQ4JaPnPtJMwUH6k0xUkgXWMd";
    string endpoint = $"{_rootUrl}/objects?read_key={_readKey}";
    string query = "%7Btype%3A%22posts%22%7D";
    Uri uri = new Uri(Uri.UnescapeDataString(endpoint + "&query=" + query));
    CosmicResponse cosmicReponse = await http.GetFromJsonAsync<CosmicResponse>(uri);
    List<Post> posts = new();
    foreach (CosmicPost c in cosmicReponse.Objects)
    {
        posts.Add(GetPostFromCosmic(c));
    }
    return posts;
}

FINAL WORDS

It turns out it was a CORS issue. As Postman doesn't enforce CORS policy, I falsely thought the problem was linked to HttpClient. I solved the problem by going through a proxy.

FF.Martin
  • 13
  • 2
  • 1
    Does this answer your question? [HttpClient problem with URLs which include curly braces](https://stackoverflow.com/questions/6741476/httpclient-problem-with-urls-which-include-curly-braces) – Luuk Dec 04 '21 at 13:04
  • Thanks @Luuk , I already tried to replace the curly braces with %7B and %7D. But HttpClient still seems to insists on sending { and }. – FF.Martin Dec 04 '21 at 13:09
  • Can you add complete code with which you get `Type error failed to fetch`? With my test I am getting `StatusCode: 401, ReasonPhrase: 'Unauthorized',....` – Luuk Dec 04 '21 at 13:36
  • I added the real readKey for easier testing. You may be getting 401 for using my dummy readKey (xxxx) instead of the good one. – FF.Martin Dec 04 '21 at 15:39
  • I was not asking for the key, but for some code to know more about your error "Type error failed to fetch". I know I will get an Unauthorized, when trying to get something I do not have the proper key for, But simple getting this error means the request it probably correct (on my side, that why I am asking for your code)? – Luuk Dec 04 '21 at 15:45
  • OK ! Added the "full code". Thanks for your help ! – FF.Martin Dec 04 '21 at 16:03
  • I think you should change `Uri uri ....` to `string uri = endpoint + "&query=" + query;` Because (parts of?) your uri are already encoded, like the `%7B`. – Luuk Dec 04 '21 at 16:28
  • 1
    You want to **escape**, not unescape. You should edit the title. – Luke Vo Dec 04 '21 at 17:21
  • I have to admit escape/unescape made my head spin after a while... – FF.Martin Dec 04 '21 at 17:28

1 Answers1

1

Better let the library does the work for you. Use NameValueCollection returned by HttpUtility.ParseQueryString (note that they return a different derived type HttpValueCollection which you cannot initialize by yourself). See more here.

Since you didn't give the class definition, I switched the type to object so the test code can run:

static async Task<object> GetAllPosts()
{
    HttpClient http = new();
    string _rootUrl = "https://api.cosmicjs.com/v2/buckets/ffm-blog-blog";
    string _readKey = "1VQXp8053OlOs05XcoWX3L46TEQ4JaPnPtJMwUH6k0xUkgXWMd";

    var query = HttpUtility.ParseQueryString("");
    query.Add("read_key", _readKey);
    query.Add("query", "{\"type\":\"posts\"}");

    string endpoint = $"{_rootUrl}/objects?" + query.ToString();
    return (await http.GetFromJsonAsync<object>(endpoint))!;    
}
Luke Vo
  • 17,859
  • 21
  • 105
  • 181
  • Thanks for suggesting using NameValueCollection, it made my code much cleaner. The URI is as clean as expected (no { or } in it). Sadly it still won't work and the URI still has unencoded curly braces AFTER passing through the httpClient. It seems like httpclient re-encodes the URI (but only the curly braces). – FF.Martin Dec 04 '21 at 18:25
  • I tested the above code, it works for me. Are you sure it's the same problem? – Luke Vo Dec 05 '21 at 00:14
  • Thanks a lot. Turns out it was a CORS problem related to the V2 of the API. Postman or console apps don't enforce CORS and worked fine with your code (or even mine). Httpclient wasn't wasn't to blame... – FF.Martin Dec 05 '21 at 05:52