Jak wysłać JSON na serwer za pomocą C #?

269

Oto kod, którego używam:

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

Kiedy to uruchamiam, zawsze pojawia się 500 wewnętrzny błąd serwera.

Co ja robię źle?

Arsen Zahray
źródło
1
Najpierw upewnij się, że publikowane dane są zgodne z oczekiwaniami serwera.
LB
wygląda na to, że publikowałem nieprawidłowe dane ...
Arsen Zahray
Dla ułatwienia pracy możesz także dodać bibliotekę json do swojego studia wizualnego
Alireza Tabatabaeian
@Arsen - Serwer nie powinien ulec awarii z nieprawidłowo sformatowanymi danymi. Złóż raport o błędzie.
jww

Odpowiedzi:

396

Sposób, w jaki to robię i działa to:

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

Napisałem bibliotekę, aby wykonać to zadanie w prostszy sposób, jest tutaj: https://github.com/ademargomes/JsonRequest

Mam nadzieję, że to pomoże.

Ademar
źródło
3
Myślę, że wierszem ciągu json powinien być: ciąg json = "{\" użytkownik \ ": \" test \ "," + "\" hasło \ ": \" bla \ "}"; Wygląda na to, że brakuje Ci \
Dream Lane
3
Zawsze używaj „application / json” (chyba że z jakiegoś innego powodu potrzebny jest tekst / json, na przykład: entwicklungsgedanken.de/2008/06/06/… ). Creding idzie: stackoverflow.com/questions/477816/... .
Janów
34
Myślałem, że streamWriter.Flush (); i streamWriter.Close (); nie jest konieczne, ponieważ znajdujesz się w bloku używającym. Pod koniec używanego bloku program zapisujący strumień i tak zostanie zamknięty.
Ruchira,
1
Nie buduj JSON ręcznie. Łatwo jest popełniać błędy, które pozwalają na wstrzyknięcie JSON.
Florian Winter
5
@ user3772108 Patrz stackoverflow.com/a/16380064/2279059 . Użyj biblioteki JSON, takiej jak Newtonsoft JSON.Net, i wyrenderuj ciąg JSON z obiektu lub użyj serializacji. Rozumiem, że zostało to tutaj pominięte ze względu na prostotę (chociaż wzrost prostoty jest minimalny), ale formatowanie strukturalnych ciągów danych (JSON, XML, ...) jest zbyt niebezpieczne, aby zrobić to nawet w trywialnych scenariuszach i zachęcić ludzi do kopiowania takiego kodu .
Florian Winter,
149

Roztwór Ademar może być poprawiona poprzez wykorzystanie JavaScriptSerializerjest Serializesposób zapewnić niejawnego konwersji obiektu JSON.

Dodatkowo można wykorzystać usingdomyślną funkcjonalność instrukcji, aby pominąć jawne wywołanie Flushi 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
źródło
1
Jaka jest różnica między tym a powyższym kodem, czy czegoś brakuje?
JMK
16
Wykorzystuje metodę Serialize JavaScriptSerializer do utworzenia prawidłowego JSON zamiast ręcznego tworzenia.
Sean Anderson
Zobacz odpowiedź Jean F. poniżej - powinien to być komentarz. Uważaj, aby typ zawartości application/jsonbył prawidłowy.
Lucas
@SeanAnderson Wciąż mam błąd „Nie można połączyć się ze zdalnym serwerem” Błąd.
ralphgabb
3
@LuzanBaral potrzebujesz tylko zestawu: System.Web.Extensions
Norbrecht
60

Ten HttpClienttyp jest nowszą implementacją niż WebClienti HttpWebRequest.

Możesz po prostu użyć następujących wierszy.

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

wprowadź opis zdjęcia tutaj

Jeśli potrzebujesz HttpClientwięcej niż raz, zaleca się utworzenie tylko jednego wystąpienia i ponowne użycie go lub użycie nowego HttpClientFactory.

NtFreX
źródło
5
Mała uwaga na temat HttpClient, ogólny konsensus jest taki, że nie powinieneś go wyrzucać. Nawet implementuje IDisposable, obiekt jest bezpieczny dla wątków i przeznaczony do ponownego użycia. stackoverflow.com/questions/15705092/...
Jean F.
1
@JeanF. Hej Dzięki za wkład. Jak już zauważyłem, powinieneś utworzyć tylko jedną instancję lub użyć HttpClientFactory. Nie przeczytałem wszystkich odpowiedzi w powiązanym numerze, ale myślę, że wymaga aktualizacji, ponieważ nie wspomina o fabryce.
NtFreX
33

Poza postem Seana nie trzeba zagnieżdżać instrukcji using. By usingw StreamWriter będzie zaczerwieniona i zamknięta na końcu bloku, więc nie trzeba jawnie wywołać Flush()i Close()metod:

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
źródło
1
teraz ta odpowiedź i odpowiedź Seana Andersona są dokładnie takie same, ponieważ Sean zredagował swój post.
faza
Hej, to wspaniale. Dzięki. Ale jak przekażemy dane, jeśli będziemy mieć węzły potomne na naszym telefonie?
user2728409
1
Serializator może obsługiwać węzły potomne w json - wystarczy podać prawidłowy obiekt json.
David Clarke
14

Jeśli chcesz zadzwonić asynchronicznie, użyj

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
źródło
3
Dziękujemy za opublikowanie tego rozwiązania Vivek. W naszym scenariuszu wypróbowaliśmy inne rozwiązanie w tym poście i zakończyliśmy przegląd System.Threading wyjątki w naszej aplikacji, z powodu tego, co zakładam, były synchroniczne posty blokujące wątki. Twój kod rozwiązał nasz problem.
Ken Palmer,
Zauważ, że prawdopodobnie nie musisz konwertować na bajty. Powinieneś być w stanie to zrobić postStream.Write(postData);- w zależności od interfejsu API może być konieczne użycie request.ContentType = "application/json";zamiast niego text/json.
vapcguy
13

Zadbaj o typ zawartości, którego używasz:

application/json

Źródła:

RFC4627

Inny post

Jean F.
źródło
11

Niedawno wymyśliłem znacznie prostszy sposób opublikowania JSON, z dodatkowym krokiem konwersji z modelu w mojej aplikacji. Pamiętaj, że musisz wykonać model [JsonObject] dla swojego kontrolera, aby uzyskać wartości i wykonać konwersję.

Żądanie:

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

Model:

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

Po stronie serwera:

[HttpPut]     
public async Task<HttpResponseMessage> PutApi([FromBody]MyModel model)
{
    ...
    ... 
}
Dustin
źródło
6

Ta opcja nie jest wymieniona:

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
źródło
2
Ta opcja nie jest już dostępna od .Net 4.5.2. patrz tutaj stackoverflow.com/a/40525794/2161568
Downhillski
Głosuj na powyższy komentarz - ponieważ nie jest dostępny, prawdopodobnie powinien usunąć odpowiedź.
NovaDev,
1
To nie jest wystarczający powód, by głosować za tą odpowiedzią, ponieważ nie wszyscy używają najnowszych wersji .net i dlatego jest to poprawna odpowiedź.
Ellisan,
4

Innym i czystym sposobem na osiągnięcie tego jest użycie HttpClient w następujący sposób:

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
źródło
4
Pomocne, jednak PostAsJsonAsyncnie jest już dostępne od .NET 4.5.2. Użyj PostAsynczamiast tego. Więcej tutaj
Zachary Keener
HttpClient zasadniczo nie powinien być używany w takim usingstwierdzeniu
p3tch
Myślę, że implementuje IDisposableinterfejs z jakiegoś powodu
Dima Daron
4

OSTRZEŻENIE! Mam bardzo mocne zdanie na ten temat.

Obecni klienci sieci .NET nie są przyjaźni dla programistów! WebRequest i WebClient są doskonałymi przykładami „jak sfrustrować programistę”. Są pełne i skomplikowane w pracy; gdy wszystko, co chcesz zrobić, to proste żądanie Post w C #. HttpClient w jakiś sposób rozwiązuje te problemy, ale nadal nie spełnia tego zadania . Poza tym dokumentacja Microsoftu jest zła… naprawdę zła; chyba że chcesz przeszukiwać strony techniczne.

Open-source na ratunek.Istnieją trzy doskonałe, bezpłatne biblioteki NuGet jako alternatywy. Dzięki Bogu! Wszystkie są dobrze obsługiwane, udokumentowane i tak, łatwa - korekta… bardzo łatwa - praca.

Nie ma między nimi wiele, ale dałbym ServiceStack. Tekst lekką przewagę…

  • Gwiazdy Github są mniej więcej takie same.
  • Otwarte problemy i, co ważne, jak szybko jakieś problemy zostały zamknięte? ServiceStack odbiera nagrodę za najszybsze rozwiązanie problemu i brak otwartych problemów.
  • Dokumentacja? Wszystkie mają świetną dokumentację; jednak ServiceStack przenosi go na wyższy poziom i jest znany ze swojego „złotego standardu” w zakresie dokumentacji.

Ok - więc jak wygląda żądanie postu w JSON w ServiceStack.Text?

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

To jest jedna linia kodu. Zwięzłe i łatwe! Porównaj powyższe z bibliotekami HTTP platformy .NET.

Programowanie za pomocą Marka
źródło
3

W końcu wywołałem w trybie synchronizacji, włączając .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
źródło
1

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

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

Użyj ASCII zamiast UFT8

użytkownik3280472
źródło
2
brzmi jak niezły pomysł, czy coś mi brakuje?
CyberFox
JSON może zawierać znaki UTF8, wydaje się to okropnym pomysłem.
Adrian Smith