Come pubblicare JSON su un server utilizzando C #?


269

Ecco il codice che sto 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;

Quando lo eseguo, visualizzo sempre 500 errori del server interno.

Che cosa sto facendo di sbagliato?


1
Innanzitutto, assicurati che i dati che pubblichi siano quelli previsti dal server.
LB

in realtà sembra che stia pubblicando dati non validi ...
Arsen Zahray il

Per facilità di lavoro puoi aggiungere anche la libreria JSON al tuo Visual Studio
Alireza Tabatabaeian,

@Arsen: il server non deve arrestarsi in modo anomalo con dati non validi. Invia una segnalazione di bug.
JWW

Risposte:


396

Il modo in cui lo faccio e funziona è:

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

Ho scritto una libreria per eseguire questo compito in modo più semplice, è qui: https://github.com/ademargomes/JsonRequest

Spero che sia d'aiuto.


3
Penso che la linea di stringa json dovrebbe essere: stringa json = "{\" user \ ": \" test \ "," + "\" password \ ": \" bla \ "}"; Sembra che manchi un \
Dream Lane il

3
Utilizzare sempre "application / json" (a meno che per qualche altro motivo non sia necessario text / json per esempio: entwicklungsgedanken.de/2008/06/06/… ). Creding va a: stackoverflow.com/questions/477816/... .
Yaniv,

34
Avrei pensato che streamWriter.Flush (); e streamWriter.Close (); non è necessario poiché ci si trova all'interno di un blocco usando. Alla fine del blocco using, lo stream writer si chiuderà comunque.
Ruchira,

1
Non compilare JSON manualmente. È facile commettere errori che consentano l'iniezione di JSON.
Florian Winter,

5
@ user3772108 Vedere stackoverflow.com/a/16380064/2279059 . Utilizzare una libreria JSON, come Newtonsoft JSON.Net, e eseguire il rendering della stringa JSON da un oggetto o utilizzare la serializzazione. Capisco che questo è stato omesso qui per semplicità (anche se il guadagno di semplicità è minimo), ma la formattazione di stringhe di dati strutturati (JSON, XML, ...) è troppo pericolosa per farlo anche in scenari banali e per incoraggiare le persone a copiare tale codice .
Florian Winter,

149

La soluzione di Ademar può essere migliorata sfruttando JavaScriptSerializeril Serializemetodo per fornire la conversione implicita dell'oggetto in JSON.

Inoltre, è possibile sfruttare la usingfunzionalità predefinita dell'istruzione per omettere esplicitamente la chiamata Flushe 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();
}

1
Qual è la differenza tra questo e il codice sopra, mi sto perdendo qualcosa?
JMK,

16
Questo utilizza il metodo Serialize di JavaScriptSerializer per creare JSON valido invece di crearlo a mano.
Sean Anderson,

Vedi la risposta di Jean F sotto - dovrebbe essere un commento. Fare attenzione con il tipo di contenuto application/jsonè corretto.
Lucas,

@SeanAnderson Continuo ad avere l'errore "Impossibile connettersi al server remoto".
ralphgabb,

3
@LuzanBaral hai solo bisogno di un assemblaggio: System.Web.Extensions
Norbrecht

60

Il HttpClienttipo è un'implementazione più recente rispetto a WebCliente HttpWebRequest.

Puoi semplicemente usare le seguenti righe.

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

inserisci qui la descrizione dell'immagine

Quando è necessario HttpClientpiù di una volta, si consiglia di creare solo un'istanza e riutilizzarla o utilizzare la nuova HttpClientFactory.


5
Una piccola nota su HttpClient, il consenso generale è che non dovresti eliminarlo. Anche implementa IDisposable l'oggetto è Thread-Safe e dovrebbe essere riutilizzato. stackoverflow.com/questions/15705092/…
Jean F.

1
@JeanF. Ehi, grazie per l'input. Come ho già notato, dovresti creare solo un'istanza o utilizzare il HttpClientFactory. Non ho letto tutte le risposte nel problema collegato ma penso che debba essere aggiornato in quanto non menziona la fabbrica.
NtFreX,

33

Oltre al post di Sean, non è necessario nidificare le dichiarazioni using. Tramite usingStreamWriter verrà scaricato e chiuso alla fine del blocco, quindi non è necessario chiamare esplicitamente i metodi Flush()e Close():

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

1
ora questa risposta e la risposta di Sean Anderson sono esattamente le stesse, poiché Sean ha modificato il suo post.
Faza,

Ehi, è fantastico. Grazie, ma come passeremo i dati se abbiamo nodi figli sul nostro json?
user2728409,

1
Il serializzatore è in grado di gestire nodi figlio in json: è sufficiente fornire un oggetto json valido.
David Clarke,

14

Se è necessario chiamare in modo asincrono, utilizzare

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

    }

3
Grazie per aver pubblicato questa soluzione Vivek. Nel nostro scenario abbiamo provato un'altra soluzione in questo post e abbiamo finito per vedere System.Threading eccezioni nella nostra applicazione, a causa di quello che presumo fossero messaggi sincroni che bloccano i thread. Il tuo codice ha risolto il nostro problema.
Ken Palmer,

Si noti che probabilmente non è necessario convertire in byte. Dovresti essere in grado di farlo postStream.Write(postData);e, a seconda dell'API, potrebbe essere necessario utilizzare un request.ContentType = "application/json";invece di text/json.
vapcguy,


11

Di recente ho trovato un modo molto più semplice di pubblicare un JSON, con l'ulteriore passaggio di conversione da un modello nella mia app. Nota che devi creare il modello [JsonObject] per il tuo controller per ottenere i valori ed eseguire la conversione.

Richiesta:

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

Modello:

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

Lato server:

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

6

Questa opzione non è menzionata:

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

2
Questa opzione non è più disponibile da .Net 4.5.2. vedi qui stackoverflow.com/a/40525794/2161568
Downhillski il

Downvote per il commento sopra - dal momento che questo non è disponibile, dovrebbe probabilmente rimuovere la risposta.
NovaDev,

1
Questa non è una buona ragione per sottovalutare questa risposta poiché non tutti usano le ultime versioni di .net e quindi questa è una risposta valida.
Ellisan,

4

Un modo diverso e pulito per raggiungere questo obiettivo è utilizzare HttpClient in questo modo:

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

4
Utile, tuttavia PostAsJsonAsyncnon è più disponibile da .NET 4.5.2. Usa PostAsyncinvece. Altro qui
Zachary Keener,

HttpClient generalmente non dovrebbe essere usato in una usingdichiarazione come questa
p3tch

Penso che implementa l' IDisposableinterfaccia per un motivo
Dima Daron,

4

AVVERTIMENTO! Ho una visione molto forte di questo argomento.

I client Web esistenti di .NET non sono adatti agli sviluppatori! WebRequest e WebClient sono ottimi esempi di "come frustrare uno sviluppatore". Sono prolissi e complicati con cui lavorare; quando tutto quello che vuoi fare è una semplice richiesta Pubblica in C #. HttpClient qualche modo a risolvere questi problemi, ma non è ancora . Inoltre la documentazione di Microsoft è pessima ... davvero pessima; a meno che non si desideri vagliare pagine e pagine di problemi tecnici.

Open-source in soccorso. Esistono tre eccellenti librerie NuGet open source e gratuite come alternative. Grazie a Dio! Questi sono tutti ben supportati, documentati e sì, facile - correzione ... super facile - con cui lavorare.

  • ServiceStack.Text : veloce, leggero e resistente.
  • RestSharp : client API REST e HTTP semplice
  • Flurl : una libreria client HTTP fluida, portatile e testabile

Non c'è molto tra loro, ma darei ServiceStack.Text il leggero vantaggio ...

  • Le stelle di Github sono all'incirca le stesse.
  • Problemi aperti e, soprattutto, quanto velocemente eventuali problemi sono stati chiusi? ServiceStack riceve il premio qui per la risoluzione dei problemi più rapida e senza problemi aperti.
  • Documentazione? Tutti hanno un'ottima documentazione; tuttavia, ServiceStack lo porta al livello successivo ed è noto per la sua "norma d'oro" per la documentazione.

Ok, quindi come appare una richiesta di post in JSON in ServiceStack.

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

Questa è una riga di codice. Conciso e facile! Confronta quanto sopra con le librerie Http di .NET.


3

Alla fine ho invocato in modalità di sincronizzazione includendo il .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");
}

1

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

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

Usa ASCII invece di UFT8


2
sembra una pessima idea, mi sto perdendo qualcosa?
CyberFox,

JSON può contenere caratteri UTF8, questa sembra un'idea terribile.
Adrian Smith,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.