How would you upload a parameter that is an array using c# webclient ?
public string CallWebService(string url, NameValueCollection parametres, string HttpVerb = "GET")
{
WebClient myWebClient = new WebClient();
byte[] myresponse = myWebClient.UploadValues(url, HttpVerb, parametres);
// on essaye de récuopérer l'encoding depuis les headers
// sinon on utilise l'encoding par defaut
Encoding encoding;
encoding = WebUtils.GetEncodingFrom(myWebClient.ResponseHeaders, Encoding.Default);
return encoding.GetString(myresponse);
}
CallWebService(url, new NameValueCollection {
{ "type_pj", "99_AU" },
{ "type_pj", "41_NC" },
{ "type_pj", "41_NC" }
}, "PATCH")
is not understood server side. It is seen as one string "99_AU,41_NC,41_NC". If I use
CallWebService(url, new NameValueCollection {
{ "type_pj[]", "99_AU" },
{ "type_pj[]", "41_NC" },
{ "type_pj[]", "41_NC" }
}, "PATCH")
it is seen as an Array but it contains only one string ["99_AU,41_NC,41_NC"]
The following curl get its right.
curl -X PATCH \ https://... \ -d 'type_pj%5B%5D=99_AU&type_pj%5B%5D=41_NC&type_pj%5B%5D=41_NC'
The servers sees it as an array of 3 strings ["99_AU","41_NC","41_NC"]
How could I achieve the same using a webclient ?
[EDIT]
It looks like I confused System.Collections.Specialized.NameValueCollection and System.Web.HttpValueCollection (which is a NameValueCollection too but with additionnal features)
var nv = new NameValueCollection {};
nv.Add("type_pj[]", "99_AU");
nv.Add("type_pj[]", "41_NC");
nv.Add("withcomma", "text,with,comma");
nv.Add("type_pj[]", "41_NC");
=> nv["type_pj[]"] = "99_AU,41_NC,41_NC" // = nv[0]
=> nv["withcomma"] = "text,with,comma" // = nv[1]
var hv = HttpUtility.ParseQueryString(string.Empty);
hv.Add("type_pj[]", "99_AU");
hv.Add("type_pj[]", "41_NC");
hv.Add("withcomma", "text,with,comma");
hv.Add("type_pj[]", "41_NC");
string resp = hv.ToString();
=> hv["type_pj[]"] = "99_AU,41_NC,41_NC" // = hv[0]
=> hv["withcomma"] = "text,with,comma" // = hv[1]
In an HttpValueCollection, when you add a new value for an existing key it looks like it concatenate the new value ti the previous but under the hood it is also stored in a array.
=> hv.GetValues("type_pj[]") = ["99_AU","41_NC","41_NC"] // !!! Array of 3 strings
=> hv.GetValues("withcomma[]") = ["text,with,comma"] // !!! Array of 1 strings
The HttpValueCollection has a ToString() method that does the url encoding for you and it preserves the array struct !
=> hv.ToString() ="type_pj%5b%5d=99_AU&type_pj%5b%5d=41_NC&type_pj%5b%5d=41_NC&withcomma=text%2cwith%2ccomma"
So the best solution is probably to use uploadString as suggested by elgonzo. But instead of using Lis you could use an HttpValueCollection which ToString() method will take care of the urlencoding for you.
The problem you are facing is due to using NamedValueCollection. If you inspect the content of your NamedValueCollection in the debugger, you will notice that it aggregates all values under just one key. This is because the name keys you feed to your NamedValueCollection are all identical.
To avoid this, you will have to generate the data payload string manually. To make things simple for the Linq Select statement below in my answer, i switch to a collection that does not organize key/value pairs depending on the uniqueness of the key. For example, you could use a list like this:
var parameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string,string>("type_pj[]", "99_AU"),
new KeyValuePair<string,string>("type_pj[]", "41_NC"),
new KeyValuePair<string,string>("type_pj[]", "41_NC")
};
Based on this collection, you could then "manually" compose the data payload string while also making sure both names and values are properly escaped:
var dataPayload = string.Join(
"&",
parameters.Select(kvp => $"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value)}")
);
(There are certainly different ways and flavors of how to do this; my example here is just an illustration of the general approach.)
The last step missing is to actually send the encoded payload string and receive the server response. Based on the code in the question, you expect a server response that is text. Fortunately, WebClient has an upload method that both accepts the data to be uploaded as string as well as returning the server response as string: UploadString
. Depending on the particular expectations of your server, you might need to specify the content type explicitly, too.
var myWebClient = new WebClient();
myWebClient.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
string responseString = myWebClient.UploadString(url, "PATCH", dataPayload);
As pointed out by @frenchone in the comments, it is also possible to generate the encoded data payload string from a NameValueCollection, albeit with a slightly more complex Linq statement:
var parameters = NameValueCollection
{
{ "type_pj[]", "99_AU" },
{ "type_pj[]", "41_NC" },
{ "type_pj[]", "41_NC" }
}
var dataPayload = string.Join(
"&",
parameters.AllKeys.SelectMany(
key => parameters.GetValues(key).Select(val => $"{Uri.EscapeDataString(key)}={Uri.EscapeDataString(val)}")
)
);