Как загрузить значения в виде массива с помощью веб-клиента C #?например items [] = "foo" items [] = "foo2" - PullRequest
1 голос
/ 19 июня 2019

Как бы вы загрузили параметр, который является массивом, используя 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")

не понимается на стороне сервера. Это видно как одна строка "99_AU, 41_NC, 41_NC". Если я использую

CallWebService(url, new NameValueCollection {
            { "type_pj[]", "99_AU" },
            { "type_pj[]", "41_NC" },
            { "type_pj[]", "41_NC" }
}, "PATCH")

он рассматривается как массив, но содержит только одну строку ["99_AU, 41_NC, 41_NC"]

Следующий локон получит свое право.

curl -X PATCH \  https://... \  -d 'type_pj%5B%5D=99_AU&type_pj%5B%5D=41_NC&type_pj%5B%5D=41_NC'

Серверы видят его как массив из 3 строк ["99_AU", "41_NC", "41_NC"]

Как я могу добиться того же, используя веб-клиента?

[EDIT]

Похоже, я перепутал System.Collections.Specialized.NameValueCollection и System.Web.HttpValueCollection (который также является NameValueCollection, но с дополнительными функциями)

        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]

В коллекции HttpValueCollection, когда вы добавляете новое значение для существующего ключа, оно выглядит так, как будто оно объединяет новое значение с предыдущим, но под капотом оно также сохраняется в массиве.

=> hv.GetValues("type_pj[]") = ["99_AU","41_NC","41_NC"] // !!! Array of 3 strings
=> hv.GetValues("withcomma[]") = ["text,with,comma"] // !!! Array of 1 strings

HttpValueCollection имеет метод ToString (), который выполняет кодировку URL для вас и сохраняет структуру массива!

=> hv.ToString() ="type_pj%5b%5d=99_AU&type_pj%5b%5d=41_NC&type_pj%5b%5d=41_NC&withcomma=text%2cwith%2ccomma"

Так что лучшим решением, вероятно, является использование uploadString, как предложено elgonzo. Но вместо использования Lis вы можете использовать HttpValueCollection, метод ToString () которого позаботится о кодировании urlen для вас.

1 Ответ

0 голосов
/ 19 июня 2019

Проблема, с которой вы сталкиваетесь, связана с использованием NamedValueCollection . Если вы проверите содержимое вашей NamedValueCollection в отладчике, вы заметите, что он объединяет все значения только под одним ключом. Это связано с тем, что все имена ключей, которые вы вводите в NamedValueCollection , идентичны.

Чтобы избежать этого, вам нужно будет сгенерировать строку полезных данных вручную. Чтобы упростить для оператора Linq Select ниже в моем ответе, я переключаюсь на коллекцию, которая не организует пары ключ / значение в зависимости от уникальности ключа. Например, вы можете использовать такой список:

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")
};

Основываясь на этом наборе, вы могли бы затем "вручную" составить строку полезных данных, а также убедиться, что имена и значения правильно экранированы:

var dataPayload = string.Join(
    "&",
    parameters.Select(kvp => $"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value)}")
);

(Есть, конечно, разные способы и варианты того, как это сделать; мой пример здесь просто иллюстрация общего подхода.)

Последним пропущенным шагом является фактическая отправка закодированной строки полезной нагрузки и получение ответа сервера. Исходя из кода в вопросе, вы ожидаете, что ответ сервера будет текстовым. К счастью, WebClient имеет метод загрузки, который принимает данные для загрузки в виде строки, а также возвращает ответ сервера в виде строки: UploadString. В зависимости от конкретных ожиданий вашего сервера вам может потребоваться также явно указать тип содержимого.

var myWebClient = new WebClient();
myWebClient.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
string responseString = myWebClient.UploadString(url, "PATCH", dataPayload);

Как отмечает @frenchone в комментариях, также возможно генерировать строку полезной нагрузки закодированных данных из NameValueCollection , хотя и с несколько более сложной инструкцией Linq:

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)}")
    )
);
...