2

I'm using UriBuilder to create a url for an API endpoint.

I need to add some query strings for it, and I can do that nicely with the following example:

    private async Task<string> CallAPI(string url, string queryString)
    {
        string s = "https://mysite.wendesk.com/api/v2/search/export/";
        UriBuilder uriBuild = new UriBuilder(s);
        uriBuild.Query = queryString;
        using (var result = await _HttpClient.GetAsync($"{uriBuild.Uri.ToString()}"))
        {

            if (!result.IsSuccessStatusCode)
            {
                throw new Exception("bad status code from zendesk");
            }
            return await result.Content.ReadAsStringAsync();
        }

    }

Which is easy and nice. But I need to a quite a few query strings, and depending on who's calling the function, I need varying amounts. So another solution could be something like this:

    private async Task<string> CallAPI(string url, string[] queryStrings)
    {
        string s = "https://mysite.wendesk.com/api/v2/search/export/";
        UriBuilder uriBuild = new UriBuilder(s);
        uriBuild.Query = string.Join("&", queryStrings);
        using (var result = await _HttpClient.GetAsync($"{uriBuild.Uri.ToString()}"))
        {

            if (!result.IsSuccessStatusCode)
            {
                throw new Exception("bad status code from zendesk");
            }
            return await result.Content.ReadAsStringAsync();
        }

    }

But I was wondering if there was something that could feel more "native". Perhaps something like creating a dicitionary with keys and values, so that the caller could just create a dictionary instead of hardcoding so much of the query strings in there?

penstiaGarse
  • 161
  • 7
  • 1
    _"Perhaps something like creating a dicitionary with keys and values, so that the caller could just create a dictionary instead of hardcoding so much of the query strings in there?"_ - `Dictionary` is kinda heavy - consider accepting `IEnumerable>` instead, that way your lib's consumers can still pass in a `Dictionary` but also many other collection types. – Dai Oct 10 '22 at 09:17
  • 1
    Also, don't forget to use `Uri.EscapeDataString` to escape querystring parameters, otherwise your code will break if `queryStrings` contains something like `"a=b&c=d"`. – Dai Oct 10 '22 at 09:18
  • thx! 'EscapeDataString' would just be used by calling it on the string I am passing to 'Query', right? – penstiaGarse Oct 10 '22 at 09:28
  • 1
    No, because then it will escape the `=` and `&` separators that your code intentionally adds: you need to escape individual strings (names and values, **separately**) before concatenating them. – Dai Oct 10 '22 at 09:33

1 Answers1

1

I think NameValueCollection might work for a solution like you mentioned. You can use a dynamically method.

For example:

     private Task<string> CreateQuery(NameValueCollection nvc)
{
    var values = from key in nvc.AllKeys
        from value in nvc.GetValues(key)
        select string.Format(
            "{0}={1}",
            HttpUtility.UrlEncode(key),
            HttpUtility.UrlEncode(value));
    return Task.FromResult("?" + Join("&", values));
}
Bzafer
  • 11
  • 3
  • You should use `Uri.EscapeDataString` instead of `HttpUtility.UrlEncode`: https://stackoverflow.com/a/37110034/159145 – Dai Oct 10 '22 at 10:12
  • The call to `ToArray();` is unnecessary as `String.Join` accepts an `IEnumerable`. – Dai Oct 10 '22 at 10:12