1

I'm posting data to a service that requires that I submit duplicate query string keys (ugly and not specified in any standards).

I'm using WebClient object to build the request. I'd like to keep using it since it is used frequently elsewhere in the project.

When I do this

 foreach(var f in formats)                
      client.QueryString.Add("formats", f);

I get a list &formats=format_1,format_2,format_3 which the service does not support.

Is there a better alternative than this old-school ugliness:

var extraQueryString = string.Empty;

extraQueryString += "?apiKey=" + TRANSCODE_KEY;
extraQueryString += "&fileKey=" + fileKey;

foreach (var f in formats)     
      extraQueryString += "&formats=" + f;

var response = client.UploadData(TRANSCODE_URI + "task" + extraQueryString , new byte[] { });
FlavorScape
  • 13,301
  • 12
  • 75
  • 117

2 Answers2

1

The reason for this is because the NameValueCollection separates duplicate keys with commas. You could extend the NameValueCollection and override the Get method and have it return the format you want.

public class DupeNVC: NameValueCollection
{
    private string _duplicateKey;
    public DupeNVC(string duplicateKey = null)
    {
        _duplicateKey = duplicateKey;
    }

    public override string Get(int index)
    {
        //check if duplicate key has been specified
        //if not, then call the default Get implementation
        if (!String.IsNullOrEmpty(_duplicateKey))
        {
            ArrayList list = (ArrayList)base.BaseGet(index);              

            int num = (list != null) ? list.Count : 0;
            if (num == 1)
            {
                return (string)list[0];
            }
            if (num > 1)
            {
                StringBuilder stringBuilder = new StringBuilder((string)list[0]);

                for (int i = 1; i < num; i++)
                {
                    //format our string and append the duplicate key specified
                    stringBuilder.AppendFormat("&{0}=", _duplicateKey);
                    stringBuilder.Append((string)list[i]);
                }
                return stringBuilder.ToString();                   
            }
            return null;
        }
        else
           return base.Get(index);
    }

} 

You can use it like a normal NameValueCollection but if you pass in a duplicate strning in the constructor, it will look for that duplicate key and run the modified code above (otherwise it will just use the default base.Get method.

DupeNVC dnvc = new DupeNVC("formats");

foreach(var f in formats)     
    dnvc.Add("formats", f);

webClient.QueryString = dnvc;

This hasn't been fully tested but it should output the querystring format you want. Of course, this could be extended further by taking a collection of duplicate keys but this was just to give you an idea for your current problem.

keyboardP
  • 68,824
  • 13
  • 156
  • 205
  • I have used above code to seperate comma values..i am using duplicate key it removes comma and add &key= but then also my code gives bad request error – Bokambo Oct 02 '18 at 22:41
0

Here's my take on this. WebClient essentially works like the ToString method of this class; it gets all the keys and then retrieves the values one at a time, doing a concatenate. So I override the AllKeys to return an array with repeated elements..

For example if a particular key has multiple values:

nvc["hello"] = { "a", "b", "c" }

Then my AllKeys will return an array with "hello" 3 times. WebClient will naively request it 3 times. A Dictionary tracks how many times a "hello" has been requested, and returns a different one each time (pseudo enumerator)

public class ParrotingNameValueCollection : NameValueCollection
{

    Dictionary<string, int> _indexTracker = new Dictionary<string, int>();

    public override string[] AllKeys
    {
        get
        {
            var l = new List<string>();
            foreach (var k in base.AllKeys)
            {
                foreach (var x in (ArrayList)base.BaseGet(k))
                    l.Add(k);

                _indexTracker[k] = 0;
            }
            
            return l.ToArray();
        }
    }


    public override string Get(string name)
    {

        var list = (ArrayList)base.BaseGet(name);

        var toReturn = (string)list[_indexTracker[name]];

        _indexTracker[name]++;

        return toReturn;
    }

    public override string ToString()
    {
        string delimiter = String.Empty;
        StringBuilder values = new StringBuilder();
        foreach (string name in this.AllKeys)
        {
            values.Append(delimiter);
            values.Append((name));
            values.Append("=");
            values.Append((this[name]));
            delimiter = "&";
        }

        return values.ToString();
    }
}
Caius Jard
  • 72,509
  • 5
  • 49
  • 80