Jak złożyć żądanie sieciowe HTTP POST

1132

Kanoniczny
Jak mogę złożyć żądanie HTTP i wysłać dane przy użyciu tej POST metody?

Mogę GETzłożyć wniosek, ale nie mam pojęcia, jak go złożyć POST.

Samogon
źródło

Odpowiedzi:

2163

Istnieje kilka sposobów wykonywania HTTP GETi POSTżądań:


Metoda A: HttpClient (preferowana)

Dostępny w: .NET Framework 4.5+, .NET Standard 1.1+, .NET Core 1.0+.

Jest to obecnie preferowane podejście, asynchroniczne i o wysokiej wydajności. W większości przypadków używaj wersji wbudowanej, ale w przypadku bardzo starych platform istnieje pakiet NuGet .

using System.Net.Http;

Ustawiać

Zaleca się utworzenie instancji HttpClientprzez cały okres użytkowania aplikacji i udostępnienie jej, chyba że masz konkretny powód, aby tego nie robić.

private static readonly HttpClient client = new HttpClient();

Zobacz HttpClientFactoryna wtrysku zależność rozwiązania.


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

Metoda B: Biblioteki stron trzecich

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

Jest to nowsza biblioteka z płynnym API, testująca pomocników, używa HttpClient pod maską i jest przenośna. Jest dostępny za pośrednictwem 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();
    

Metoda C: HttpWebRequest (niezalecana do nowej pracy)

Dostępny w: .NET Framework 1.1+, .NET Standard 2.0+, .NET Core 1.0+. W .NET Core jest głównie kompatybilny - pakuje się HttpClient, jest mniej wydajny i nie otrzyma nowych funkcji.

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();
    

Metoda D: WebClient (niezalecany do nowej pracy)

To jest opakowanie HttpWebRequest. Porównaj zHttpClient .

Dostępny w: .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");
    }
Evan Mulawski
źródło
2
@Lloyd:HttpWebResponse response = (HttpWebResponse)HttpWReq.GetResponse();
Evan Mulawski
2
Dlaczego nawet używasz ASCII? Co jeśli ktoś potrzebuje xml z UTF-8?
Gero,
8
Nienawidzę bić martwego konia, ale powinieneś to zrobićresponse.Result.Content.ReadAsStringAsync()
David S.
13
dlaczego powiedziałeś, że WebRequest i WebClient są dziedzictwem? MSDN nie mówi, że są przestarzałe lub coś takiego. Czy coś brakuje?
Hiep,
23
@Hiep: Nie są przestarzałe, istnieją tylko nowsze (i w większości przypadków lepsze i bardziej elastyczne) sposoby wysyłania żądań internetowych. Moim zdaniem, w przypadku prostych, niekrytycznych operacji stare sposoby są w porządku - ale to zależy od ciebie i od tego, co najbardziej ci odpowiada.
Evan Mulawski,
384

Proste żądanie GET

using System.Net;

...

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

Proste żądanie 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);
}
Pavlo Neiman
źródło
15
+1 W przypadku zwykłych testów POST świetnie jest mieć tak krótki fragment kodu.
user_v
3
Tim - Jeśli klikniesz prawym przyciskiem myszy literał, którego nie można rozwiązać, znajdziesz menu kontekstowe Rozwiąż, które zawiera czynności umożliwiające dodanie instrukcji Używanie. Jeśli menu kontekstowe Rozwiąż nie pojawia się, oznacza to, że musisz najpierw dodać odniesienia.
Cameron Wilby
Zaakceptowałem twoją odpowiedź jako dobrą, ponieważ jest ona znacznie prostsza i jaśniejsza.
Hooch
13
Chciałbym dodać, że zmienną odpowiedzi dla żądania POST jest tablica bajtów. Aby uzyskać odpowiedź ciągu, wystarczy wykonać Encoding.ASCII.GetString (odpowiedź); (using System.Text)
Sindre
1
Ponadto możesz wysłać nieco złożoną tablicę $ _POST ['użytkownik'] jako: data ["użytkownik [nazwa użytkownika]"] = "moja nazwa użytkownika"; data ["user [password]"] = "myPassword";
Bimal Poudel
68

MSDN ma próbkę.

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
źródło
Z jakiegoś powodu nie zadziałało, gdy wysyłałem dużą ilość danych
AnKing
26

To jest kompletny działający przykład wysyłania / odbierania danych w formacie JSON, użyłem 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
źródło
8

Oto kilka naprawdę dobrych odpowiedzi. Pozwól, że opublikuję inny sposób ustawiania nagłówków za pomocą WebClient (). Pokażę też, jak ustawić klucz 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);
Adam
źródło
Przydatne, dzięki. BTW Wygląda na to, że powyższa technika ustawiania właściwości nagłówka działa również w przypadku starszego (przestarzałego?) Podejścia HttpWebRequest. np. myReq.Headers [HttpRequestHeader.Authorization] = $ "Basic {credentials}";
Zeek2
6

To rozwiązanie wykorzystuje wyłącznie standardowe wywołania platformy .NET.

Przetestowany:

  • W użyciu w aplikacji WPF dla przedsiębiorstw. Używa asynchronizacji / oczekiwania na uniknięcie blokowania interfejsu użytkownika.
  • Kompatybilny z .NET 4.5+.
  • Testowany bez parametrów (wymaga „GET” za sceną).
  • Testowany z parametrami (wymaga „POST” za scenami).
  • Testowany ze standardową stroną internetową, taką jak Google.
  • Testowane z wewnętrzną usługą internetową opartą na Javie.

Odniesienie:

// Add a Reference to the assembly System.Web

Kod:

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

Aby zadzwonić bez parametrów (używa „GET” za sceną):

 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.
 }

Aby wywołać z parametrami (używa „POST” za scenami):

 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.
 }
Contango
źródło
6

Proste rozwiązanie (jedno-liniowe, bez sprawdzania błędów, bez oczekiwania na odpowiedź) Znalazłem do tej pory:

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

Używaj ostrożnie!

Ohad Cohen
źródło
5
To całkiem źle. Nie polecam tego, ponieważ nie ma żadnej obsługi błędów, a debugowanie to ból. Dodatkowo istnieje już świetna odpowiedź na to pytanie.
Hooch,
1
@Hooch inni mogą być zainteresowani tego typu odpowiedziami, nawet jeśli nie są one najlepsze.
Mitulát báti
Zgadzam się, jedynym kontekstem, w którym byłoby to przydatne, jest gra w golfa kodowego i kto gra w C #;)
Extragorey
4

Podczas korzystania z przestrzeni nazw Windows.Web.Http dla POST zamiast FormUrlEncodedContent piszemy HttpFormUrlEncodedContent. Odpowiedź jest również typem HttpResponseMessage. Reszta jest, jak napisał Evan Mulawski.

S4NNY1
źródło
4

Jeśli lubisz płynne API, możesz użyć Tiny.RestClient . Jest dostępny w 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
źródło
1

Dlaczego nie jest to całkowicie trywialne? Wykonanie żądania nie jest, a zwłaszcza nie dotyczy wyników, i wydaje się, że w grę wchodzi również kilka błędów .NET - zobacz Błąd w HttpClient.GetAsync powinien zgłosić wyjątek WebException, a nie TaskCanceledException

Skończyło się na tym kodzie:

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

Spowoduje to wykonanie instrukcji GET lub POST, jeśli postBufferma wartość zero lub nie

jeśli sukces jest prawdziwy, odpowiedź będzie dostępna ResponseAsString

Sukces jeśli jest fałszywe, można sprawdzić WebExceptionStatus, HttpStatusCodei ResponseAsStringspróbować zobaczyć, co poszło źle.

Kofifus
źródło
0

W rdzeniu .net możesz wykonać połączenie po zakończeniu z następującym kodem, tutaj dodałem kilka dodatkowych funkcji do tego kodu, dzięki czemu Twój kod może działać za serwerem proxy i poświadczeniami sieci, jeśli takie istnieją, tutaj również wspominam, że możesz zmienić kodowanie Twoja wiadomość. Mam nadzieję, że to wszystko wyjaśni i pomoże ci w kodowaniu.

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;
        }
Syed Fahad Ali
źródło