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?


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.


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

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.


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

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

    }

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


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

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

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

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.


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

1

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

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

Użyj ASCII zamiast UFT8


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.