Comment publier JSON sur un serveur en utilisant C #?

269

Voici le code que j'utilise:

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

Lorsque j'exécute cela, j'obtiens toujours 500 erreurs de serveur interne.

Qu'est-ce que je fais mal?

Arsen Zahray
la source
1
Tout d'abord, assurez-vous que les données que vous publiez correspondent à ce que le serveur attend.
LB
en fait, on dirait que je publiais des données invalides ...
Arsen Zahray
Pour faciliter le travail, vous pouvez également ajouter la bibliothèque json à votre studio visuel
Alireza Tabatabaeian
@Arsen - Le serveur ne doit pas planter avec des données mal formées. Déposer un rapport de bogue.
jww

Réponses:

396

La façon dont je le fais et fonctionne est:

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

J'ai écrit une bibliothèque pour effectuer cette tâche de manière plus simple, c'est ici: https://github.com/ademargomes/JsonRequest

J'espère que ça aide.

Ademar
la source
3
Je pense que la ligne de chaîne json devrait être: string json = "{\" user \ ": \" test \ "," + "\" password \ ": \" bla \ "}"; Il semble que vous manquiez un \
Dream Lane
3
Utilisez toujours "application / json" (sauf pour une autre raison, text / json est nécessaire, par exemple: entwicklungsgedanken.de/2008/06/06/… ). Creding va à: stackoverflow.com/questions/477816/… .
Yaniv
34
J'aurais pensé que streamWriter.Flush (); et streamWriter.Close (); n'est pas nécessaire car vous vous trouvez dans un bloc using. À la fin du bloc using, l'écrivain de flux se fermera quand même.
Ruchira
1
Ne construisez pas JSON manuellement. Il est facile de faire des erreurs qui permettent une injection JSON.
Florian Winter
5
@ user3772108 Voir stackoverflow.com/a/16380064/2279059 . Utilisez une bibliothèque JSON, telle que Newtonsoft JSON.Net, et restituez la chaîne JSON à partir d'un objet, ou utilisez la sérialisation. Je comprends que cela a été omis ici pour des raisons de simplicité (bien que le gain de simplicité soit minime), mais le formatage des chaînes de données structurées (JSON, XML, ...) est trop dangereux pour le faire même dans des scénarios triviaux et pour encourager les gens à copier un tel code .
Florian Winter
149

La solution d'Ademar peut être améliorée en exploitant JavaScriptSerializerla Serializeméthode de pour fournir une conversion implicite de l'objet en JSON.

De plus, il est possible de tirer parti des usingfonctionnalités par défaut de l' instruction afin d'omettre explicitement d'appeler Flushet 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
la source
1
Quelle est la différence entre ce code et le code ci-dessus, est-ce que je manque quelque chose?
JMK
16
Cela utilise la méthode Serialize de JavaScriptSerializer pour créer un JSON valide au lieu de le fabriquer à la main.
Sean Anderson
Voir la réponse de Jean F ci-dessous - devrait être un commentaire. Faites attention avec le type de contenu application/jsonest correct.
Lucas
@SeanAnderson Je continue d'avoir "Impossible de se connecter au serveur distant" Erreur.
ralphgabb
3
@LuzanBaral vous avez juste besoin d'un assemblage: System.Web.Extensions
Norbrecht
60

Le HttpClienttype est une implémentation plus récente que le WebClientet HttpWebRequest.

Vous pouvez simplement utiliser les lignes suivantes.

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

entrez la description de l'image ici

Lorsque vous avez besoin de HttpClientplusieurs fois, il est recommandé de ne créer qu'une seule instance et de la réutiliser ou d'utiliser la nouvelle HttpClientFactory.

NtFreX
la source
5
Une petite note sur HttpClient, le consensus général est que vous ne devez pas le jeter. Même s'il implémente IDisposable, l'objet est Thread-Safe et destiné à être réutilisé. stackoverflow.com/questions/15705092/…
Jean F.
1
@JeanF. Hé Merci pour la contribution. Comme je l'ai déjà noté, vous ne devez créer qu'une seule instance ou utiliser le HttpClientFactory. Je n'ai pas lu toutes les réponses dans le problème lié, mais je pense qu'il doit être mis à jour car il ne mentionne pas l'usine.
NtFreX
33

Suite à la publication de Sean, il n'est pas nécessaire d'imbriquer les instructions using. Par usingStreamWriter, il sera vidé et fermé à la fin du bloc, donc pas besoin d'appeler explicitement les méthodes Flush()et 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();
}
David Clarke
la source
1
maintenant cette réponse et la réponse de Sean Anderson sont exactement les mêmes, car Sean a édité son post.
Faza
Hé, c'est tellement bien, merci, mais comment allons-nous transmettre les données si nous avons des nœuds enfants sur notre json?
user2728409
1
Le sérialiseur peut gérer les nœuds enfants dans json - il vous suffit de lui fournir un objet json valide.
David Clarke
14

Si vous devez appeler de manière asynchrone, utilisez

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
la source
3
Merci d'avoir posté cette solution Vivek. Dans notre scénario, nous avons essayé une autre solution dans ce message et nous avons fini par voir des exceptions System.Threading dans notre application, en raison de ce que je suppose être des messages synchrones bloquant les threads. Votre code a résolu notre problème.
Ken Palmer
Notez que vous n'avez probablement pas besoin de convertir en octets. Vous devriez pouvoir le faire postStream.Write(postData);- et selon l'API, vous devrez peut-être utiliser un request.ContentType = "application/json";au lieu de text/json.
vapcguy
13

Prenez soin du type de contenu que vous utilisez:

application/json

Sources :

RFC4627

Autre poste

Jean F.
la source
11

J'ai récemment trouvé un moyen beaucoup plus simple de publier un JSON, avec l'étape supplémentaire de conversion à partir d'un modèle dans mon application. Notez que vous devez créer le modèle [JsonObject] pour que votre contrôleur obtienne les valeurs et effectue la conversion.

Demande:

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

Modèle:

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

Du côté serveur:

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

Cette option n'est pas mentionnée:

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
la source
2
Cette option n'est plus disponible depuis .Net 4.5.2. voir ici stackoverflow.com/a/40525794/2161568
Downhillski
Downvote par le commentaire ci-dessus - puisque ce n'est pas disponible, devrait probablement supprimer la réponse.
NovaDev
1
Ce n'est pas une bonne raison de voter contre cette réponse car tout le monde n'utilise pas les dernières versions de .net et c'est donc une réponse valide.
Ellisan
4

Une façon différente et propre d'y parvenir est d'utiliser HttpClient comme ceci:

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
la source
4
Utile, cependant, PostAsJsonAsyncn'est plus disponible depuis .NET 4.5.2. Utilisez PostAsyncplutôt. Plus ici
Zachary Keener
HttpClient ne devrait généralement pas être utilisé dans une usingdéclaration comme celle-ci
p3tch
Je pense qu'il implémente l' IDisposableinterface pour une raison
Dima Daron
4

AVERTISSEMENT! J'ai une opinion très forte sur ce sujet.

Les clients Web existants de .NET ne sont pas adaptés aux développeurs! WebRequest et WebClient sont des exemples de "comment frustrer un développeur". Ils sont verbeux et compliqués à travailler; quand tout ce que vous voulez faire est une simple demande de publication en C #. HttpClient va dans une certaine mesure pour résoudre ces problèmes, mais il est toujours . En plus de cela, la documentation de Microsoft est mauvaise… vraiment mauvaise; sauf si vous souhaitez parcourir les pages et les pages de texte technique.

Open-source à la rescousse. Il existe trois excellentes bibliothèques NuGet libres et open source comme alternatives. Dieu merci! Tout cela est bien pris en charge, documenté et oui, facile - correction… super facile - avec lequel travailler.

  • ServiceStack.Text - rapide, léger et résistant.
  • RestSharp - Client API REST et HTTP simple
  • Flurl - une bibliothèque client HTTP fluide, portable et testable

Il n'y a pas grand-chose entre eux, mais je donnerais à ServiceStack.Text le léger avantage…

  • Les étoiles Github sont à peu près les mêmes.
  • Problèmes ouverts et, surtout, à quelle vitesse les problèmes ont-ils été fermés? ServiceStack remporte le prix ici pour la résolution de problème la plus rapide et aucun problème ouvert.
  • Documentation? Tous ont une excellente documentation; cependant, ServiceStack le porte au niveau supérieur et est connu pour son «standard d'or» pour la documentation.

Ok - alors à quoi ressemble une demande de publication dans JSON dans ServiceStack.Text?

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

C'est une ligne de code. Concis et facile! Comparez ce qui précède aux bibliothèques Http de .NET.

Programmation avec Mark
la source
3

J'ai finalement invoqué en mode sync en incluant le .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
la source
1

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

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

Utilisez ASCII au lieu de UFT8

user3280472
la source
2
sonne comme une assez mauvaise idée, est-ce que je manque quelque chose?
CyberFox
JSON peut contenir des caractères UTF8, cela semble être une idée terrible.
Adrian Smith