¿Cómo publicar JSON en un servidor usando C #?

269

Aquí está el código que estoy usando:

// create a request
HttpWebRequest request = (HttpWebRequest)
WebRequest.Create(url); request.KeepAlive = false;
request.ProtocolVersion = HttpVersion.Version10;
request.Method = "POST";


// turn our request string into a byte stream
byte[] postBytes = Encoding.UTF8.GetBytes(json);

// this is important - make sure you specify type this way
request.ContentType = "application/json; charset=UTF-8";
request.Accept = "application/json";
request.ContentLength = postBytes.Length;
request.CookieContainer = Cookies;
request.UserAgent = currentUserAgent;
Stream requestStream = request.GetRequestStream();

// now send it
requestStream.Write(postBytes, 0, postBytes.Length);
requestStream.Close();

// grab te response and print it out to the console along with the status code
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string result;
using (StreamReader rdr = new StreamReader(response.GetResponseStream()))
{
    result = rdr.ReadToEnd();
}

return result;

Cuando ejecuto esto, siempre recibo 500 errores internos del servidor.

¿Qué estoy haciendo mal?

Arsen Zahray
fuente
1
Primero, asegúrese de que los datos que publique sean los que el servidor espera.
LB
En realidad, parece que estaba publicando datos no válidos ...
Arsen Zahray
Para facilitar el trabajo, también puede agregar la biblioteca json a su estudio visual
Alireza Tabatabaeian
@Arsen: el servidor no debe bloquearse con datos mal formados. Presentar un informe de error.
jww

Respuestas:

396

La forma en que lo hago y funciona es:

var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://url");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";

using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
    string json = "{\"user\":\"test\"," +
                  "\"password\":\"bla\"}";

    streamWriter.Write(json);
}

var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
    var result = streamReader.ReadToEnd();
}

Escribí una biblioteca para realizar esta tarea de una manera más simple, está aquí: https://github.com/ademargomes/JsonRequest

Espero eso ayude.

Ademar
fuente
3
Creo que la línea de cadena json debería ser: string json = "{\" user \ ": \" test \ "," + "\" password \ ": \" bla \ "}"; Parece que te falta un \
Dream Lane
3
Utilice siempre "application / json" (a menos que por alguna otra razón se necesite text / json, por ejemplo: entwicklungsgedanken.de/2008/06/06/… ). El crédito va a: stackoverflow.com/questions/477816/… .
Yaniv
34
Hubiera pensado el streamWriter.Flush (); y streamWriter.Close (); no es necesario ya que estás dentro de un bloque de uso. Al final del bloque de uso, el escritor de secuencias se cerrará de todos modos.
Ruchira
1
No compile JSON manualmente. Es fácil cometer errores que permiten la inyección JSON.
Florian Winter
55
@ user3772108 Consulte stackoverflow.com/a/16380064/2279059 . Use una biblioteca JSON, como Newtonsoft JSON.Net, y renderice la cadena JSON de un objeto, o use la serialización. Entiendo que esto se omitió aquí por simplicidad (aunque la ganancia de simplicidad es mínima), pero formatear cadenas de datos estructurados (JSON, XML, ...) es demasiado peligroso para hacerlo incluso en escenarios triviales y para alentar a las personas a copiar dicho código .
Florian Winter
149

La solución de Ademar se puede mejorar aprovechando JavaScriptSerializerel Serializemétodo para proporcionar la conversión implícita del objeto a JSON.

Además, es posible aprovechar la usingfuncionalidad predeterminada de la declaración para omitir llamar explícitamente Flushy Close.

var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://url");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";

using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
    string json = new JavaScriptSerializer().Serialize(new
                {
                    user = "Foo",
                    password = "Baz"
                });

    streamWriter.Write(json);
}

var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
    var result = streamReader.ReadToEnd();
}
Sean Anderson
fuente
1
¿Cuál es la diferencia entre este y el código anterior? ¿Me estoy perdiendo algo?
JMK
16
Esto utiliza el método Serialize de JavaScriptSerializer para crear un JSON válido en lugar de crearlo a mano.
Sean Anderson
Vea la respuesta de Jean F a continuación, debería ser un comentario. Tenga cuidado con el tipo de contenido application/jsones correcto.
Lucas
@SeanAnderson Sigo teniendo el error "No se puede conectar al servidor remoto".
ralphgabb
3
@LuzanBaral solo necesita un ensamblaje: System.Web.Extensions
Norbrecht
60

El HttpClienttipo es una implementación más nueva que laWebClient y HttpWebRequest.

Simplemente puede usar las siguientes líneas.

string myJson = "{'Username': 'myusername','Password':'pass'}";
using (var client = new HttpClient())
{
    var response = await client.PostAsync(
        "http://yourUrl", 
         new StringContent(myJson, Encoding.UTF8, "application/json"));
}

ingrese la descripción de la imagen aquí

Cuando necesite su HttpClientmás de una vez, se recomienda crear solo una instancia y reutilizarla o usar la nueva HttpClientFactory.

NtFreX
fuente
55
Una pequeña nota sobre HttpClient, el consenso general es que no debe deshacerse de él. Incluso implementa IDisposable, el objeto es Thread-Safe y está destinado a ser reutilizado. stackoverflow.com/questions/15705092/…
Jean F.
1
@JeanF. Gracias por el aporte. Como ya he señalado, solo debe crear una instancia o usar el HttpClientFactory. No leí todas las respuestas en el problema vinculado, pero creo que necesita actualizarse, ya que no menciona la fábrica.
NtFreX
33

Además de la publicación de Sean, no es necesario anidar las declaraciones de uso. Por usingel StreamWriter que se vaciará y cerrado en el extremo del bloque así que no hay necesidad de explícitamente llamar al Flush()y Close()métodos:

var request = (HttpWebRequest)WebRequest.Create("http://url");
request.ContentType = "application/json";
request.Method = "POST";

using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
    string json = new JavaScriptSerializer().Serialize(new
                {
                    user = "Foo",
                    password = "Baz"
                });

    streamWriter.Write(json);
}

var response = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
        var result = streamReader.ReadToEnd();
}
David Clarke
fuente
1
ahora esta respuesta y la respuesta de Sean Anderson son exactamente las mismas, ya que Sean ha editado su publicación.
faza
Hola, esto es genial. Gracias. Pero, ¿cómo vamos a pasar datos si tenemos nodos secundarios en nuestro json?
user2728409
1
El serializador puede manejar nodos secundarios en json; solo tiene que proporcionarle un objeto json válido.
David Clarke
14

Si necesita llamar de forma asíncrona, use

var request = HttpWebRequest.Create("http://www.maplegraphservices.com/tokkri/webservices/updateProfile.php?oldEmailID=" + App.currentUser.email) as HttpWebRequest;
            request.Method = "POST";
            request.ContentType = "text/json";
            request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);

private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
    {
        HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
        // End the stream request operation

        Stream postStream = request.EndGetRequestStream(asynchronousResult);


        // Create the post data
        string postData = JsonConvert.SerializeObject(edit).ToString();

        byte[] byteArray = Encoding.UTF8.GetBytes(postData);


        postStream.Write(byteArray, 0, byteArray.Length);
        postStream.Close();

        //Start the web request
        request.BeginGetResponse(new AsyncCallback(GetResponceStreamCallback), request);
    }

    void GetResponceStreamCallback(IAsyncResult callbackResult)
    {
        HttpWebRequest request = (HttpWebRequest)callbackResult.AsyncState;
        HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callbackResult);
        using (StreamReader httpWebStreamReader = new StreamReader(response.GetResponseStream()))
        {
            string result = httpWebStreamReader.ReadToEnd();
            stat.Text = result;
        }

    }
Vivek Maskara
fuente
3
Gracias por publicar esta solución Vivek. En nuestro escenario, probamos otra solución en esta publicación, y terminamos viendo System.Threading excepciones en nuestra aplicación, debido a lo que supongo que fueron publicaciones síncronas que bloquean hilos. Su código resolvió nuestro problema.
Ken Palmer
Tenga en cuenta que probablemente no tenga que convertir a bytes. Debería poder hacerlo postStream.Write(postData);, y dependiendo de la API, podría tener que usar un en request.ContentType = "application/json";lugar de text/json.
Vapcguy
11

Recientemente se me ocurrió una forma mucho más simple de publicar un JSON, con el paso adicional de convertir de un modelo en mi aplicación. Tenga en cuenta que debe crear el modelo [JsonObject] para que su controlador obtenga los valores y realice la conversión.

Solicitud:

 var model = new MyModel(); 

 using (var client = new HttpClient())
 {
     var uri = new Uri("XXXXXXXXX"); 
     var json = new JavaScriptSerializer().Serialize(model);
     var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
     var response = await Client.PutAsync(uri,stringContent).Result;
     ...
     ...
  }

Modelo:

[JsonObject]
[Serializable]
public class MyModel
{
    public Decimal Value { get; set; }
    public string Project { get; set; }
    public string FilePath { get; set; }
    public string FileName { get; set; }
}

Lado del servidor:

[HttpPut]     
public async Task<HttpResponseMessage> PutApi([FromBody]MyModel model)
{
    ...
    ... 
}
Dustin
fuente
6

Esta opción no se menciona:

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri("http://localhost:9000/");
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    var foo = new User
    {
        user = "Foo",
        password = "Baz"
    }

    await client.PostAsJsonAsync("users/add", foo);
}
Centro
fuente
2
Esta opción ya no está disponible desde .Net 4.5.2. ver aquí stackoverflow.com/a/40525794/2161568
Downhillski el
Voto negativo según el comentario anterior, ya que esto no está disponible, probablemente debería eliminar la respuesta.
NovaDev
1
Esa no es una buena razón para rechazar esta respuesta, ya que no todos usan las últimas versiones de .net y, por lo tanto, esta es una respuesta válida.
Ellisan
4

Una forma diferente y limpia de lograr esto es mediante el uso de HttpClient de esta manera:

public async Task<HttpResponseMessage> PostResult(string url, ResultObject resultObject)
{
    using (var client = new HttpClient())
    {
        HttpResponseMessage response = new HttpResponseMessage();
        try
        {
            response = await client.PostAsJsonAsync(url, resultObject);
        }
        catch (Exception ex)
        {
            throw ex
        }
        return response;
     }
}
Dima Daron
fuente
44
Útil, sin embargo, PostAsJsonAsyncya no está disponible desde .NET 4.5.2. Usar en su PostAsynclugar. Más aquí
Zachary Keener
HttpClient generalmente no debería usarse en una usingdeclaración como esta
p3tch
Creo que implementa la IDisposableinterfaz por una razón
Dima Daron
4

¡ADVERTENCIA! Tengo una opinión muy fuerte sobre este tema.

¡Los clientes web existentes de .NET no son compatibles con los desarrolladores! WebRequest y WebClient son excelentes ejemplos de "cómo frustrar a un desarrollador". Son detallados y complicados para trabajar; cuando todo lo que quiere hacer es una simple solicitud de publicación en C #. HttpClient ayuda a resolver estos problemas, pero aún se queda corto. Además de eso, la documentación de Microsoft es mala ... realmente mala; a menos que quiera examinar páginas y páginas de publicidad técnica.

De código abierto al rescate. Hay tres excelentes bibliotecas NuGet gratuitas de código abierto como alternativas. ¡Gracias a dios! Todos estos están bien soportados, documentados y sí, fáciles de corregir ... súper fáciles de trabajar.

No hay mucho entre ellos, pero le daría a ServiceStack.Text una ligera ventaja ...

  • Las estrellas de Github son más o menos lo mismo.
  • Problemas abiertos y, lo que es más importante, ¿con qué rapidez se cerraron los problemas? ServiceStack recibe el premio aquí por la resolución de problemas más rápida y sin problemas abiertos.
  • ¿Documentación? Todos tienen excelente documentación; sin embargo, ServiceStack lo lleva al siguiente nivel y es conocido por su 'estándar de oro' para la documentación.

Ok, entonces, ¿cómo se ve una solicitud de publicación en JSON dentro de ServiceStack.Text?

var response = "http://example.org/login"
    .PostJsonToUrl(new Login { Username="admin", Password="mypassword" });

Esa es una línea de código. Conciso y fácil! Compare lo anterior con las bibliotecas Http de .NET.

Programando con Mark
fuente
3

Finalmente invoqué en modo de sincronización incluyendo el .Result

HttpResponseMessage response = null;
try
{
    using (var client = new HttpClient())
    {
       response = client.PostAsync(
        "http://localhost:8000/....",
         new StringContent(myJson,Encoding.UTF8,"application/json")).Result;
    if (response.IsSuccessStatusCode)
        {
            MessageBox.Show("OK");              
        }
        else
        {
            MessageBox.Show("NOK");
        }
    }
}
catch (Exception ex)
{
    MessageBox.Show("ERROR");
}
lgturrez
fuente
1

var data = Encoding.ASCII.GetBytes(json);

byte[] postBytes = Encoding.UTF8.GetBytes(json);

Use ASCII en lugar de UFT8

usuario3280472
fuente
2
Suena como una muy mala idea, ¿me estoy perdiendo algo?
CyberFox
JSON puede contener caracteres UTF8, esto parece una idea terrible.
Adrian Smith