Как сделать веб-запрос HTTP POST

1136

Canonical
Как я могу сделать HTTP-запрос и отправить некоторые данные, используя POST метод?

Я могу сделать GETзапрос, но я не знаю, как сделать POSTзапрос.

самогон
источник

Ответы:

2167

Есть несколько способов выполнить HTTP GETи POSTзапросы:


Метод A: HttpClient (предпочтительный)

Доступно в: .NET Framework 4.5+, .NET Standard 1.1+, .NET Core 1.0+.

В настоящее время это предпочтительный подход, он асинхронный и высокопроизводительный. В большинстве случаев используйте встроенную версию, но для очень старых платформ есть пакет NuGet .

using System.Net.Http;

Настроить

Рекомендуется создать его экземпляр на весь HttpClientсрок службы вашего приложения и поделиться им, если у вас нет особых причин не делать этого.

private static readonly HttpClient client = new HttpClient();

Смотрите HttpClientFactoryдля решения инъекции зависимости .


  • POST

    var values = new Dictionary<string, string>
    {
        { "thing1", "hello" },
        { "thing2", "world" }
    };
    
    var content = new FormUrlEncodedContent(values);
    
    var response = await client.PostAsync("http://www.example.com/recepticle.aspx", content);
    
    var responseString = await response.Content.ReadAsStringAsync();
  • GET

    var responseString = await client.GetStringAsync("http://www.example.com/recepticle.aspx");

Метод Б: Сторонние библиотеки

RestSharp

  • POST

     var client = new RestClient("http://example.com");
     // client.Authenticator = new HttpBasicAuthenticator(username, password);
     var request = new RestRequest("resource/{id}");
     request.AddParameter("thing1", "Hello");
     request.AddParameter("thing2", "world");
     request.AddHeader("header", "value");
     request.AddFile("file", path);
     var response = client.Post(request);
     var content = response.Content; // Raw content as string
     var response2 = client.Post<Person>(request);
     var name = response2.Data.Name;

Flurl.Http

Это более новая библиотека, которая свободно использует API, тестирует помощников, использует HttpClient под капотом и является переносимой. Это доступно через NuGet .

    using Flurl.Http;

  • POST

    var responseString = await "http://www.example.com/recepticle.aspx"
        .PostUrlEncodedAsync(new { thing1 = "hello", thing2 = "world" })
        .ReceiveString();
  • GET

    var responseString = await "http://www.example.com/recepticle.aspx"
        .GetStringAsync();

Метод C: HttpWebRequest (не рекомендуется для новой работы)

Доступно в: .NET Framework 1.1+, .NET Standard 2.0+, .NET Core 1.0+. В .NET Core это главным образом для совместимости - это обертывание HttpClient, менее производительное и не получит новые функции.

using System.Net;
using System.Text;  // For class Encoding
using System.IO;    // For StreamReader

  • POST

    var request = (HttpWebRequest)WebRequest.Create("http://www.example.com/recepticle.aspx");
    
    var postData = "thing1=" + Uri.EscapeDataString("hello");
        postData += "&thing2=" + Uri.EscapeDataString("world");
    var data = Encoding.ASCII.GetBytes(postData);
    
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = data.Length;
    
    using (var stream = request.GetRequestStream())
    {
        stream.Write(data, 0, data.Length);
    }
    
    var response = (HttpWebResponse)request.GetResponse();
    
    var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
  • GET

    var request = (HttpWebRequest)WebRequest.Create("http://www.example.com/recepticle.aspx");
    
    var response = (HttpWebResponse)request.GetResponse();
    
    var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

Метод D: WebClient (не рекомендуется для новой работы)

Это обертка вокруг HttpWebRequest. Сравните сHttpClient .

Доступно в: .NET Framework 1.1+, NET Standard 2.0+,.NET Core 2.0+

using System.Net;
using System.Collections.Specialized;

  • POST

    using (var client = new WebClient())
    {
        var values = new NameValueCollection();
        values["thing1"] = "hello";
        values["thing2"] = "world";
    
        var response = client.UploadValues("http://www.example.com/recepticle.aspx", values);
    
        var responseString = Encoding.Default.GetString(response);
    }
  • GET

    using (var client = new WebClient())
    {
        var responseString = client.DownloadString("http://www.example.com/recepticle.aspx");
    }
Эван Мулавски
источник
2
@Lloyd:HttpWebResponse response = (HttpWebResponse)HttpWReq.GetResponse();
Эван Мулавски
2
Почему вы вообще используете ASCII? Что делать, если кому-то нужен XML с UTF-8?
Геро
8
Я ненавижу бить мертвую лошадь, но вы должны сделатьresponse.Result.Content.ReadAsStringAsync()
Дэвид С.
13
почему вы сказали, что WebRequest и WebClient являются устаревшими? MSDN не говорит, что они устарели или что-то еще. Я что-то пропустил?
Hiep
23
@Hiep: Они не устарели, есть только новые (и в большинстве случаев, лучшие и более гибкие) способы создания веб-запросов. На мой взгляд, для простых некритических операций старые способы просто хороши - но это зависит от вас и от того, что вам наиболее удобно.
Эван Мулавски
385

Простой запрос GET

using System.Net;

...

using (var wb = new WebClient())
{
    var response = wb.DownloadString(url);
}

Простой POST-запрос

using System.Net;
using System.Collections.Specialized;

...

using (var wb = new WebClient())
{
    var data = new NameValueCollection();
    data["username"] = "myUser";
    data["password"] = "myPassword";

    var response = wb.UploadValues(url, "POST", data);
    string responseInString = Encoding.UTF8.GetString(response);
}
Павло Нейман
источник
16
+1 Для обычного материала POST здорово иметь такой короткий кусок кода.
user_v
3
Тим - Если вы щелкнете правой кнопкой мыши по литералу, который не может быть разрешен, вы найдете контекстное меню Resolve, которое содержит действия для добавления операторов Using для вас. Если контекстное меню «Разрешить» не отображается, это означает, что сначала нужно добавить ссылки.
Кэмерон Уилби
Я принял ваш ответ как хороший, потому что он намного проще и понятнее.
Хуч
13
Я хотел бы добавить, что переменная ответа на запрос POST является байтовым массивом. Чтобы получить строковый ответ, вы просто делаете Encoding.ASCII.GetString (response); (с использованием System.Text)
Синдр
1
Кроме того, вы можете отправить немного сложный массив $ _POST ['user'] как: data ["user [username]"] = "myUsername"; data ["user [password]"] = "myPassword";
Бимал Пудель
68

У MSDN есть образец.

using System;
using System.IO;
using System.Net;
using System.Text;

namespace Examples.System.Net
{
    public class WebRequestPostExample
    {
        public static void Main()
        {
            // Create a request using a URL that can receive a post. 
            WebRequest request = WebRequest.Create("http://www.contoso.com/PostAccepter.aspx");
            // Set the Method property of the request to POST.
            request.Method = "POST";
            // Create POST data and convert it to a byte array.
            string postData = "This is a test that posts this string to a Web server.";
            byte[] byteArray = Encoding.UTF8.GetBytes(postData);
            // Set the ContentType property of the WebRequest.
            request.ContentType = "application/x-www-form-urlencoded";
            // Set the ContentLength property of the WebRequest.
            request.ContentLength = byteArray.Length;
            // Get the request stream.
            Stream dataStream = request.GetRequestStream();
            // Write the data to the request stream.
            dataStream.Write(byteArray, 0, byteArray.Length);
            // Close the Stream object.
            dataStream.Close();
            // Get the response.
            WebResponse response = request.GetResponse();
            // Display the status.
            Console.WriteLine(((HttpWebResponse)response).StatusDescription);
            // Get the stream containing content returned by the server.
            dataStream = response.GetResponseStream();
            // Open the stream using a StreamReader for easy access.
            StreamReader reader = new StreamReader(dataStream);
            // Read the content.
            string responseFromServer = reader.ReadToEnd();
            // Display the content.
            Console.WriteLine(responseFromServer);
            // Clean up the streams.
            reader.Close();
            dataStream.Close();
            response.Close();
        }
    }
}
Otávio Décio
источник
По какой-то причине это не сработало, когда я отправлял большой объем данных
AnKing
26

Это полный рабочий пример отправки / получения данных в формате JSON, я использовал Visual Studio 2013 Express Edition:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;

namespace ConsoleApplication1
{
    class Customer
    {
        public string Name { get; set; }
        public string Address { get; set; }
        public string Phone { get; set; }
    }

    public class Program
    {
        private static readonly HttpClient _Client = new HttpClient();
        private static JavaScriptSerializer _Serializer = new JavaScriptSerializer();

        static void Main(string[] args)
        {
            Run().Wait();
        }

        static async Task Run()
        {
            string url = "http://www.example.com/api/Customer";
            Customer cust = new Customer() { Name = "Example Customer", Address = "Some example address", Phone = "Some phone number" };
            var json = _Serializer.Serialize(cust);
            var response = await Request(HttpMethod.Post, url, json, new Dictionary<string, string>());
            string responseText = await response.Content.ReadAsStringAsync();

            List<YourCustomClassModel> serializedResult = _Serializer.Deserialize<List<YourCustomClassModel>>(responseText);

            Console.WriteLine(responseText);
            Console.ReadLine();
        }

        /// <summary>
        /// Makes an async HTTP Request
        /// </summary>
        /// <param name="pMethod">Those methods you know: GET, POST, HEAD, etc...</param>
        /// <param name="pUrl">Very predictable...</param>
        /// <param name="pJsonContent">String data to POST on the server</param>
        /// <param name="pHeaders">If you use some kind of Authorization you should use this</param>
        /// <returns></returns>
        static async Task<HttpResponseMessage> Request(HttpMethod pMethod, string pUrl, string pJsonContent, Dictionary<string, string> pHeaders)
        {
            var httpRequestMessage = new HttpRequestMessage();
            httpRequestMessage.Method = pMethod;
            httpRequestMessage.RequestUri = new Uri(pUrl);
            foreach (var head in pHeaders)
            {
                httpRequestMessage.Headers.Add(head.Key, head.Value);
            }
            switch (pMethod.Method)
            {
                case "POST":
                    HttpContent httpContent = new StringContent(pJsonContent, Encoding.UTF8, "application/json");
                    httpRequestMessage.Content = httpContent;
                    break;

            }

            return await _Client.SendAsync(httpRequestMessage);
        }
    }
}
Ivanzinho
источник
8

Здесь есть несколько действительно хороших ответов. Позвольте мне опубликовать другой способ установки заголовков с помощью WebClient (). Я также покажу вам, как установить ключ API.

        var client = new WebClient();
        string credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(userName + ":" + passWord));
        client.Headers[HttpRequestHeader.Authorization] = $"Basic {credentials}";
        //If you have your data stored in an object serialize it into json to pass to the webclient with Newtonsoft's JsonConvert
        var encodedJson = JsonConvert.SerializeObject(newAccount);

        client.Headers.Add($"x-api-key:{ApiKey}");
        client.Headers.Add("Content-Type:application/json");
        try
        {
            var response = client.UploadString($"{apiurl}", encodedJson);
            //if you have a model to deserialize the json into Newtonsoft will help bind the data to the model, this is an extremely useful trick for GET calls when you have a lot of data, you can strongly type a model and dump it into an instance of that class.
            Response response1 = JsonConvert.DeserializeObject<Response>(response);
Адам
источник
Полезно, спасибо. Кстати, похоже, что описанная выше методика установки свойств заголовка также работает для более старого (устаревшего?) Подхода HttpWebRequest. например, myReq.Headers [HttpRequestHeader.Authorization] = $ "Basic {credentials}";
Zeek2
6

В этом решении используются только стандартные вызовы .NET.

Проверено:

  • Используется в корпоративном приложении WPF. Использует async / await, чтобы избежать блокировки пользовательского интерфейса.
  • Совместим с .NET 4.5+.
  • Протестировано без параметров (требуется «GET» за кулисами).
  • Протестировано с параметрами (требуется «POST» за кулисами).
  • Протестировано на стандартной веб-странице, такой как Google.
  • Протестировано с внутренним веб-сервисом на основе Java.

Ссылка:

// Add a Reference to the assembly System.Web

Код:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;

private async Task<WebResponse> CallUri(string url, TimeSpan timeout)
{
    var uri = new Uri(url);
    NameValueCollection rawParameters = HttpUtility.ParseQueryString(uri.Query);
    var parameters = new Dictionary<string, string>();
    foreach (string p in rawParameters.Keys)
    {
        parameters[p] = rawParameters[p];
    }

    var client = new HttpClient { Timeout = timeout };
    HttpResponseMessage response;
    if (parameters.Count == 0)
    {
        response = await client.GetAsync(url);
    }
    else
    {
        var content = new FormUrlEncodedContent(parameters);
        string urlMinusParameters = uri.OriginalString.Split('?')[0]; // Parameters always follow the '?' symbol.
        response = await client.PostAsync(urlMinusParameters, content);
    }
    var responseString = await response.Content.ReadAsStringAsync();

    return new WebResponse(response.StatusCode, responseString);
}

private class WebResponse
{
    public WebResponse(HttpStatusCode httpStatusCode, string response)
    {
        this.HttpStatusCode = httpStatusCode;
        this.Response = response;
    }
    public HttpStatusCode HttpStatusCode { get; }
    public string Response { get; }
}

Для вызова без параметров (используется «GET» за кадром):

 var timeout = TimeSpan.FromSeconds(300);
 WebResponse response = await this.CallUri("http://www.google.com/", timeout);
 if (response.HttpStatusCode == HttpStatusCode.OK)
 {
     Console.Write(response.Response); // Print HTML.
 }

Для вызова с параметрами (используется «POST» за кулисами):

 var timeout = TimeSpan.FromSeconds(300);
 WebResponse response = await this.CallUri("http://example.com/path/to/page?name=ferret&color=purple", timeout);
 if (response.HttpStatusCode == HttpStatusCode.OK)
 {
     Console.Write(response.Response); // Print HTML.
 }
Контанго
источник
6

Простое (однострочное, без проверки ошибок, без ожидания ответа) решение, которое я нашел до сих пор:

(new WebClient()).UploadStringAsync(new Uri(Address), dataString);‏

Используйте с осторожностью!

Охад Коэн
источник
5
Это довольно плохо. Я не рекомендую это, поскольку нет никакой обработки ошибок, и отладка это боль. Кроме того, на этот вопрос уже есть отличный ответ.
Хуч
1
@ Многие могут быть заинтересованы в ответах такого типа, даже если они не самые лучшие.
Митулат Бати
Согласен, единственный контекст, в котором это было бы полезно, - это игра в код и кто играет в C #;)
Extragorey
4

При использовании пространства имен Windows.Web.Http для POST вместо FormUrlEncodedContent мы пишем HttpFormUrlEncodedContent. Также ответ является типом HttpResponseMessage. Остальное как записал Эван Мулавски.

S4NNY1
источник
4

Если вам нравится свободный API, вы можете использовать Tiny.RestClient . Это доступно в NuGet .

var client = new TinyRestClient(new HttpClient(), "http://MyAPI.com/api");
// POST
var city = new City() { Name = "Paris", Country = "France" };
// With content
var response = await client.PostRequest("City", city)
                           .ExecuteAsync<bool>();
user8803505
источник
1

Почему это не совсем тривиально? Выполнение запроса не является и, особенно, не связано с результатами, и, похоже, также присутствуют некоторые ошибки .NET - см. Ошибка в HttpClient.GetAsync должен вызывать WebException, а не TaskCanceledException

Я закончил с этим кодом:

static async Task<(bool Success, WebExceptionStatus WebExceptionStatus, HttpStatusCode? HttpStatusCode, string ResponseAsString)> HttpRequestAsync(HttpClient httpClient, string url, string postBuffer = null, CancellationTokenSource cts = null) {
    try {
        HttpResponseMessage resp = null;

        if (postBuffer is null) {
            resp = cts is null ? await httpClient.GetAsync(url) : await httpClient.GetAsync(url, cts.Token);

        } else {
            using (var httpContent = new StringContent(postBuffer)) {
                resp = cts is null ? await httpClient.PostAsync(url, httpContent) : await httpClient.PostAsync(url, httpContent, cts.Token);
            }
        }

        var respString = await resp.Content.ReadAsStringAsync();
        return (resp.IsSuccessStatusCode, WebExceptionStatus.Success, resp.StatusCode, respString);

    } catch (WebException ex) {
        WebExceptionStatus status = ex.Status;
        if (status == WebExceptionStatus.ProtocolError) {
            // Get HttpWebResponse so that you can check the HTTP status code.
            using (HttpWebResponse httpResponse = (HttpWebResponse)ex.Response) {
                return (false, status, httpResponse.StatusCode, httpResponse.StatusDescription);
            }
        } else {
            return (false, status, null, ex.ToString()); 
        }

    } catch (TaskCanceledException ex) {
        if (cts is object && ex.CancellationToken == cts.Token) {
            // a real cancellation, triggered by the caller
            return (false, WebExceptionStatus.RequestCanceled, null, ex.ToString());
        } else {
            // a web request timeout (possibly other things!?)
            return (false, WebExceptionStatus.Timeout, null, ex.ToString());
        }

    } catch (Exception ex) {
        return (false, WebExceptionStatus.UnknownError, null, ex.ToString());
    }
}

Это будет делать GET или POST в зависимости от того, postBufferявляется ли ноль или нет

если успех верен, то ответ будет в ResponseAsString

если успех является ложным , вы можете проверить WebExceptionStatus, HttpStatusCodeи ResponseAsStringпопытаться увидеть , что пошло не так.

kofifus
источник
0

В ядре .net вы можете выполнять пост-вызов с помощью следующего кода, здесь я добавил некоторые дополнительные функции к этому коду, чтобы ваш код мог работать за прокси-сервером и с сетевыми учетными данными, если таковые имеются, также здесь я упоминаю, что вы можете изменить кодировку твое сообщение. Я надеюсь, что это все объясняет и поможет вам в кодировании.

HttpClient client = GetHttpClient(_config);

        if (headers != null)
        {
            foreach (var header in headers)
            {
                client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value);
            }
        }

        client.BaseAddress = new Uri(baseAddress);

        Encoding encoding = Encoding.UTF8;


        var result = await client.PostAsync(url, new StringContent(body, encoding, "application/json")).ConfigureAwait(false);
        if (result.IsSuccessStatusCode)
        {
            return new RequestResponse { severity = "Success", httpResponse = result.Content.ReadAsStringAsync().Result, StatusCode = result.StatusCode };
        }
        else
        {
            return new RequestResponse { severity = "failure", httpResponse = result.Content.ReadAsStringAsync().Result, StatusCode = result.StatusCode };
        }


 public HttpClient GetHttpClient(IConfiguration _config)
        {
            bool ProxyEnable = Convert.ToBoolean(_config["GlobalSettings:ProxyEnable"]);

            HttpClient client = null;
            if (!ProxyEnable)
            {
                client = new HttpClient();
            }
            else
            {
                string ProxyURL = _config["GlobalSettings:ProxyURL"];
                string ProxyUserName = _config["GlobalSettings:ProxyUserName"];
                string ProxyPassword = _config["GlobalSettings:ProxyPassword"];
                string[] ExceptionURL = _config["GlobalSettings:ExceptionURL"].Split(';');
                bool BypassProxyOnLocal = Convert.ToBoolean(_config["GlobalSettings:BypassProxyOnLocal"]);
                bool UseDefaultCredentials = Convert.ToBoolean(_config["GlobalSettings:UseDefaultCredentials"]);

                WebProxy proxy = new WebProxy
                {
                    Address = new Uri(ProxyURL),
                    BypassProxyOnLocal = BypassProxyOnLocal,
                    UseDefaultCredentials = UseDefaultCredentials,
                    BypassList = ExceptionURL,
                    Credentials = new NetworkCredential(ProxyUserName, ProxyPassword)

                };

                HttpClientHandler handler = new HttpClientHandler { Proxy = proxy };
                client = new HttpClient(handler,true);
            }
            return client;
        }
Сайед Фахад Али
источник