Cómo hacer una solicitud web HTTP POST

1135

Canonical
¿Cómo puedo hacer una solicitud HTTP y enviar algunos datos usando el POST método?

Puedo hacer una GETsolicitud, pero no tengo idea de cómo hacer una POSTsolicitud.

Bebida alcohólica
fuente

Respuestas:

2166

Hay varias formas de realizar HTTP GETy POSTsolicitudes:


Método A: HttpClient (preferido)

Disponible en: .NET Framework 4.5+, .NET Standard 1.1+, .NET Core 1.0+.

Actualmente es el enfoque preferido, y es asíncrono y de alto rendimiento. Use la versión incorporada en la mayoría de los casos, pero para plataformas muy antiguas hay un paquete NuGet .

using System.Net.Http;

Preparar

Se recomienda crear una instancia HttpClientpara la vida útil de su aplicación y compartirla a menos que tenga una razón específica para no hacerlo.

private static readonly HttpClient client = new HttpClient();

Ver HttpClientFactorypara una solución de inyección de dependencia .


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

Método B: bibliotecas de terceros

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

Es una biblioteca más nueva que tiene una API fluida, prueba de ayuda, usa HttpClient debajo del capó y es portátil. Está disponible a través de 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();
    

Método C: HttpWebRequest (no recomendado para nuevos trabajos)

Disponible en: .NET Framework 1.1+, .NET Standard 2.0+, .NET Core 1.0+. En .NET Core, es principalmente por compatibilidad: se ajusta HttpClient, es menos eficiente y no obtendrá nuevas funciones.

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

Método D: WebClient (no recomendado para nuevos trabajos)

Esta es una envoltura alrededor HttpWebRequest. Comparar conHttpClient .

Disponible en: .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
fuente
2
@Lloyd:HttpWebResponse response = (HttpWebResponse)HttpWReq.GetResponse();
Evan Mulawski el
2
¿Por qué incluso usas ASCII? ¿Qué pasa si alguien necesita un xml con UTF-8?
Gero
8
Odio vencer a un caballo muerto, pero deberías hacerloresponse.Result.Content.ReadAsStringAsync()
David S.
13
¿Por qué dijiste que WebRequest y WebClient son heredados? MSDN no dice que están en desuso ni nada. ¿Me estoy perdiendo de algo?
Hiep
23
@Hiep: No están en desuso, solo hay formas más nuevas (y en la mayoría de los casos, mejores y más flexibles) de hacer solicitudes web. En mi opinión, para operaciones simples y no críticas, las viejas formas están bien, pero depende de usted y de lo que le resulte más cómodo.
Evan Mulawski
385

Solicitud GET simple

using System.Net;

...

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

Solicitud POST simple

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
fuente
16
+1 Para cosas POST normales, es genial tener un código tan corto.
user_v
3
Tim: si hace clic con el botón derecho en el literal que no se puede resolver, encontrará un menú contextual Resolver, que contiene acciones para agregar las declaraciones de Uso por usted. Si el menú contextual Resolver no aparece, significa que primero debe agregar referencias.
Cameron Wilby el
Acepté su respuesta como buena porque es mucho más simple y clara.
Hooch
13
Me gustaría agregar que la variable de respuesta para la solicitud POST es una matriz de bytes. Para obtener la respuesta de cadena, solo debe hacer Encoding.ASCII.GetString (respuesta); (usando System.Text)
Sindre
1
Además, puede enviar una matriz compleja de bits $ _POST ['user'] como: data ["user [username]"] = "myUsername"; data ["user [password]"] = "myPassword";
Bimal Poudel
68

MSDN tiene una muestra.

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
fuente
Por alguna razón no funcionó cuando estaba enviando una gran cantidad de datos
AnKing
26

Este es un ejemplo de trabajo completo de envío / recepción de datos en formato JSON. Utilicé 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
fuente
8

Hay algunas respuestas realmente buenas aquí. Permítanme publicar una forma diferente de configurar sus encabezados con WebClient (). También te mostraré cómo configurar una clave 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);
Adán
fuente
Útil, gracias. Por cierto, parece que la técnica anterior para establecer las propiedades del encabezado también funciona para el enfoque HttpWebRequest más antiguo (¿obsoleto?). por ejemplo, myReq.Headers [HttpRequestHeader.Authorization] = $ "Basic {credentials}";
Zeek2
6

Esta solución no usa más que llamadas estándar .NET.

Probado:

  • En uso en una aplicación WPF empresarial. Utiliza async / wait para evitar bloquear la interfaz de usuario.
  • Compatible con .NET 4.5+.
  • Probado sin parámetros (requiere un "GET" detrás de escena).
  • Probado con parámetros (requiere un "POST" detrás de escena).
  • Probado con una página web estándar como Google.
  • Probado con un servicio web interno basado en Java.

Referencia:

// Add a Reference to the assembly System.Web

Código:

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

Para llamar sin parámetros (usa un "GET" detrás de escena):

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

Para llamar con parámetros (usa un "POST" detrás de escena):

 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.
 }
Aplazamiento de pago
fuente
6

Solución simple (una línea, sin comprobación de errores, sin esperar respuesta) que he encontrado hasta ahora:

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

Usar con precaución!

Ohad Cohen
fuente
55
Eso es bastante malo. No lo recomiendo ya que no hay manejo de errores de ningún tipo y la depuración es dolorosa. Además, ya hay una gran respuesta a esta pregunta.
Hooch
1
@Hooch otros podrían estar interesados ​​en este tipo de respuestas, incluso si no es la mejor.
Mitulát báti
De acuerdo, el único contexto en el que esto sería útil es golf de código y quién juega golf en C #;)
Extragorey
4

Cuando se utiliza el espacio de nombres Windows.Web.Http , para POST en lugar de FormUrlEncodedContent, escribimos HttpFormUrlEncodedContent. Además, la respuesta es tipo HttpResponseMessage. El resto es como Evan Mulawski escribió.

S4NNY1
fuente
4

Si te gusta una API fluida, puedes usar Tiny.RestClient . Está disponible en 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>();
usuario8803505
fuente
1

¿Por qué esto no es totalmente trivial? Hacer la solicitud no es y, especialmente, no trata con los resultados y parece que también hay algunos errores de .NET involucrados - vea Bug en HttpClient.GetAsync debería lanzar WebException, no TaskCanceledException

Terminé con este código:

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

Esto hará que GET o POST dependa si postBufferes nulo o no

Si el éxito es verdadero, la respuesta estará en ResponseAsString

Si el éxito es falso se puede comprobar WebExceptionStatus, HttpStatusCodey ResponseAsStringpara tratar de ver lo que salió mal.

kofifus
fuente
0

En .net core puede realizar una llamada posterior con el siguiente código, aquí agregué algunas características adicionales a este código para que pueda hacer que su código funcione detrás de un proxy y con credenciales de red si las hay, también aquí menciono que puede cambiar la codificación de tu mensaje. Espero que esto lo explique todo y lo ayude a codificar.

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
fuente