Wie poste ich JSON mit C # auf einem Server?

269

Hier ist der Code, den ich verwende:

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

Wenn ich dies ausführe, erhalte ich immer 500 interne Serverfehler.

Was mache ich falsch?

Arsen Zahray
quelle
1
Stellen Sie zunächst sicher, dass die von Ihnen veröffentlichten Daten den Erwartungen des Servers entsprechen.
LB
Eigentlich sieht es so aus, als hätte ich ungültige Daten
gepostet
Um die Arbeit zu vereinfachen, können Sie Ihrem Visual Studio auch eine JSON- Bibliothek hinzufügen
Alireza Tabatabaeian,
@Arsen - Der Server sollte nicht mit fehlerhaften Daten abstürzen. Einen Fehlerbericht einreichen.
JWW

Antworten:

396

Ich mache es so und arbeite wie folgt:

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

Ich habe eine Bibliothek geschrieben, um diese Aufgabe einfacher auszuführen. Sie ist hier: https://github.com/ademargomes/JsonRequest

Ich hoffe es hilft.

Ademar
quelle
3
Ich denke, die json-String-Zeile sollte lauten: string json = "{" Benutzer ":" Test "," + "Passwort": "bla"} "; Es sieht so aus, als ob Ihnen
Dream Lane
3
Verwenden Sie immer "application / json" (es sei denn, aus einem anderen Grund wird Text / json benötigt, zum Beispiel: entwicklungsgedanken.de/2008/06/06/… ). Die Gutschrift geht an: stackoverflow.com/questions/477816/… .
Yaniv
34
Ich hätte gedacht, der streamWriter.Flush (); und streamWriter.Close (); ist nicht erforderlich, da Sie sich in einem using-Block befinden. Am Ende des using-Blocks wird der Stream-Writer trotzdem geschlossen.
Ruchira
1
Erstellen Sie JSON nicht manuell. Es ist leicht, Fehler zu machen, die eine JSON-Injektion ermöglichen.
Florian Winter
5
@ user3772108 Siehe stackoverflow.com/a/16380064/2279059 . Verwenden Sie eine JSON-Bibliothek wie Newtonsoft JSON.Net und rendern Sie die JSON-Zeichenfolge aus einem Objekt oder verwenden Sie die Serialisierung. Ich verstehe, dass dies hier der Einfachheit halber weggelassen wurde (obwohl der Gewinn an Einfachheit minimal ist), aber das Formatieren strukturierter Datenzeichenfolgen (JSON, XML, ...) ist zu gefährlich, um dies selbst in trivialen Szenarien zu tun und die Leute zum Kopieren solchen Codes zu ermutigen .
Florian Winter
149

Ademar-Lösung kann durch den Einsatz verbessert werden JavaScriptSerializer‚sSerialize Verfahren implizite Konvertierung des Objekts zu JSON bereitzustellen.

Darüber hinaus ist es möglich, die usingStandardfunktionalität der Anweisung zu nutzen, um das explizite Aufrufen von Flushund zu vermeiden 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
quelle
1
Was ist der Unterschied zwischen diesem und dem obigen Code? Fehlt mir etwas?
JMK
16
Hierbei wird die Serialize-Methode des JavaScriptSerializers verwendet, um gültigen JSON zu erstellen, anstatt ihn von Hand zu erstellen.
Sean Anderson
Siehe die Antwort von Jean F unten - sollte ein Kommentar sein. Achten Sie darauf, dass der Inhaltstyp application/jsonkorrekt ist.
Lucas
@ SeanAnderson Ich habe weiterhin den Fehler "Verbindung zum Remote-Server kann nicht hergestellt werden".
Ralphgabb
3
@LuzanBaral Sie brauchen nur eine Baugruppe: System.Web.Extensions
Norbrecht
60

Der HttpClientTyp ist eine neuere Implementierung als das WebClientund HttpWebRequest.

Sie können einfach die folgenden Zeilen verwenden.

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

Geben Sie hier die Bildbeschreibung ein

Wenn Sie Ihre HttpClientmehr als einmal benötigen , wird empfohlen, nur eine Instanz zu erstellen und wiederzuverwenden oder die neue zu verwenden HttpClientFactory.

NtFreX
quelle
5
Ein kleiner Hinweis zu HttpClient: Der allgemeine Konsens ist, dass Sie ihn nicht entsorgen sollten. Selbst wenn IDisposable implementiert wird, ist das Objekt threadsicher und soll wiederverwendet werden. stackoverflow.com/questions/15705092/…
Jean F.
1
@ JeanF. Hey Danke für die Eingabe. Wie ich bereits bemerkt habe, sollten Sie nur eine Instanz erstellen oder die verwenden HttpClientFactory. Ich habe nicht alle Antworten in der verlinkten Ausgabe gelesen, aber ich denke, es muss aktualisiert werden, da die Fabrik nicht erwähnt wird.
NtFreX
33

Neben Seans Beitrag ist es nicht erforderlich, die using-Anweisungen zu verschachteln. Durch usingden StreamWriter wird er am Ende des Blocks geleert und geschlossen, sodass die Methoden Flush()und nicht explizit aufgerufen Close()werden müssen:

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
quelle
1
Jetzt sind diese Antwort und Sean Andersons Antwort genau die gleichen, wie Sean seinen Beitrag bearbeitet hat.
Faza
Hey, das ist so toll. Danke. Aber wie werden wir Daten übergeben, wenn wir untergeordnete Knoten auf unserem JSON haben?
user2728409
1
Der Serializer kann untergeordnete Knoten in JSON verarbeiten. Sie müssen lediglich ein gültiges JSON-Objekt angeben.
David Clarke
14

Wenn Sie asynchron anrufen müssen, verwenden Sie

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
quelle
3
Vielen Dank für die Veröffentlichung dieser Lösung Vivek. In unserem Szenario haben wir in diesem Beitrag eine andere Lösung ausprobiert und dabei System.Threading-Ausnahmen in unserer Anwendung angezeigt, da ich davon ausgehe, dass synchrone Beiträge Threads blockieren. Ihr Code hat unser Problem gelöst.
Ken Palmer
Beachten Sie, dass Sie wahrscheinlich nicht in Bytes konvertieren müssen. Sie sollten dazu in der Lage sein postStream.Write(postData);- und müssen je nach API möglicherweise ein request.ContentType = "application/json";anstelle von verwenden text/json.
vapcguy
13

Achten Sie auf den von Ihnen verwendeten Inhaltstyp:

application/json

Quellen:

RFC4627

Anderer Beitrag

Jean F.
quelle
11

Ich habe mir kürzlich eine viel einfachere Möglichkeit ausgedacht, einen JSON zu veröffentlichen, mit dem zusätzlichen Schritt, von einem Modell in meiner App zu konvertieren. Beachten Sie, dass Sie das Modell [JsonObject] für Ihren Controller erstellen müssen, um die Werte abzurufen und die Konvertierung durchzuführen.

Anfrage:

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

Modell:

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

Serverseite:

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

Diese Option wird nicht erwähnt:

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
quelle
2
Diese Option ist seit .Net 4.5.2 nicht mehr verfügbar. siehe hier stackoverflow.com/a/40525794/2161568
Downhillski
Downvote gemäß obigem Kommentar - da dies nicht verfügbar ist, sollte die Antwort wahrscheinlich entfernt werden.
NovaDev
1
Dies ist kein guter Grund, diese Antwort abzulehnen, da nicht jeder die neuesten Versionen von .net verwendet und dies daher eine gültige Antwort ist.
Ellisan
4

Eine andere und saubere Möglichkeit, dies zu erreichen, ist die Verwendung von HttpClient wie folgt:

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
quelle
4
Hilfreich ist jedoch PostAsJsonAsyncseit .NET 4.5.2 nicht mehr verfügbar. Verwenden Sie PostAsyncstattdessen. Mehr hier
Zachary Keener
HttpClient sollte im Allgemeinen nicht in einer usingAnweisung wie dieser verwendet werden
p3tch
Ich denke, es implementiert IDisposableSchnittstelle aus einem Grund
Dima Daron
4

WARNUNG! Ich habe eine sehr starke Meinung zu diesem Thema.

Die vorhandenen Web-Clients von .NET sind nicht entwicklerfreundlich! WebRequest & WebClient sind erstklassige Beispiele dafür, wie man einen Entwickler frustriert. Sie sind ausführlich und kompliziert zu bearbeiten; Wenn Sie nur eine einfache Post-Anfrage in C # senden möchten. HttpClient behebt diese Probleme in gewisser Weise, ist aber immer noch unzureichend. Darüber hinaus ist die Dokumentation von Microsoft schlecht… wirklich schlecht; es sei denn, Sie möchten Seiten und Seiten mit technischem Klappentext durchsuchen.

Open Source zur Rettung. Es gibt drei ausgezeichnete kostenlose Open-Source-NuGet-Bibliotheken als Alternativen. Gott sei Dank! Diese sind alle gut unterstützt, dokumentiert und ja, einfach zu korrigieren ... super einfach zu bearbeiten.

Es gibt nicht viel zwischen ihnen, aber ich würde ServiceStack.Text die leichte Kante geben ...

  • Github-Sterne sind ungefähr gleich.
  • Offene Probleme und vor allem, wie schnell Probleme behoben wurden? ServiceStack erhält hier die Auszeichnung für die schnellste Problemlösung und keine offenen Probleme.
  • Dokumentation? Alle haben eine großartige Dokumentation; ServiceStack bringt es jedoch auf die nächste Stufe und ist bekannt für seinen "goldenen Standard" für die Dokumentation.

Ok - wie sieht eine Post-Anfrage in JSON in ServiceStack.Text aus?

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

Das ist eine Codezeile. Prägnant und einfach! Vergleichen Sie die obigen Informationen mit den HTTP-Bibliotheken von .NET.

Programmieren mit Mark
quelle
3

Ich habe schließlich im Synchronisierungsmodus aufgerufen, indem ich das .Result eingefügt habe

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
quelle
1

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

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

Verwenden Sie ASCII anstelle von UFT8

user3280472
quelle
2
klingt nach einer ziemlich schlechten Idee, vermisse ich etwas?
CyberFox
JSON kann UTF8-Zeichen enthalten, dies scheint eine schreckliche Idee zu sein.
Adrian Smith