Enviar JSON via POST em C # e receber o JSON retornado?

86

Esta é minha primeira vez usando JSON, bem como System.Nete WebRequestem qualquer das minhas aplicações. Meu aplicativo deve enviar uma carga JSON, semelhante à abaixo, para um servidor de autenticação:

{
  "agent": {                             
    "name": "Agent Name",                
    "version": 1                                                          
  },
  "username": "Username",                                   
  "password": "User Password",
  "token": "xxxxxx"
}

Para criar essa carga útil, usei a JSON.NETbiblioteca. Como eu enviaria esses dados para o servidor de autenticação e receberia sua resposta JSON de volta? Aqui está o que eu vi em alguns exemplos, mas nenhum conteúdo JSON:

var http = (HttpWebRequest)WebRequest.Create(new Uri(baseUrl));
http.Accept = "application/json";
http.ContentType = "application/json";
http.Method = "POST";

string parsedContent = "Parsed JSON Content needs to go here";
ASCIIEncoding encoding = new ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(parsedContent);

Stream newStream = http.GetRequestStream();
newStream.Write(bytes, 0, bytes.Length);
newStream.Close();

var response = http.GetResponse();

var stream = response.GetResponseStream();
var sr = new StreamReader(stream);
var content = sr.ReadToEnd();

No entanto, isso parece ser muito código comparado ao uso de outras linguagens que usei no passado. Estou fazendo isso corretamente? E como obteria a resposta JSON de volta para poder analisá-la?

Obrigado, Elite.

Código Atualizado

// Send the POST Request to the Authentication Server
// Error Here
string json = await Task.Run(() => JsonConvert.SerializeObject(createLoginPayload(usernameTextBox.Text, password)));
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
using (var httpClient = new HttpClient())
{
    // Error here
    var httpResponse = await httpClient.PostAsync("URL HERE", httpContent);
    if (httpResponse.Content != null)
    {
        // Error Here
        var responseContent = await httpResponse.Content.ReadAsStringAsync();
    }
}
Hunter Mitchell
fonte
2
Você pode tentar WebClient.UploadString(JsonConvert.SerializeObjectobj(yourobj))ouHttpClient.PostAsJsonAsync
LB de

Respostas:

128

Eu comecei a usar a biblioteca HttpClient para consultar APIs RESTful, pois o código é muito simples e totalmente assíncrono.

(Editar: Adicionando JSON da questão para maior clareza)

{
  "agent": {                             
    "name": "Agent Name",                
    "version": 1                                                          
  },
  "username": "Username",                                   
  "password": "User Password",
  "token": "xxxxxx"
}

Com duas classes que representam a estrutura JSON que você postou, que podem ser assim:

public class Credentials
{
    [JsonProperty("agent")]
    public Agent Agent { get; set; }

    [JsonProperty("username")]
    public string Username { get; set; }

    [JsonProperty("password")]
    public string Password { get; set; }

    [JsonProperty("token")]
    public string Token { get; set; }
}

public class Agent
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("version")]
    public int Version { get; set; }
}

você poderia ter um método como este, que faria sua solicitação POST:

var payload = new Credentials { 
    Agent = new Agent { 
        Name = "Agent Name",
        Version = 1 
    },
    Username = "Username",
    Password = "User Password",
    Token = "xxxxx"
};

// Serialize our concrete class into a JSON String
var stringPayload = await Task.Run(() => JsonConvert.SerializeObject(payload));

// Wrap our JSON inside a StringContent which then can be used by the HttpClient class
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");

using (var httpClient = new HttpClient()) {

    // Do the actual request and await the response
    var httpResponse = await httpClient.PostAsync("http://localhost/api/path", httpContent);

    // If the response contains content we want to read it!
    if (httpResponse.Content != null) {
        var responseContent = await httpResponse.Content.ReadAsStringAsync();

        // From here on you could deserialize the ResponseContent back again to a concrete C# type using Json.Net
    }
}
Kai Eichinger
fonte
5
perfeito, mas o que é esperar Task.run (()?
Hunter Mitchell
21
Você não deve usar Task.Run em métodos de limite de CPU síncronos, pois você está apenas disparando um novo thread sem nenhum benefício!
Stephen Foster
2
Você não precisa digitar o JsonPropertypara cada propriedade. Basta usar o CamelCasePropertyNamesContractResolver integrado do Json.Net ou um customizadoNamingStrategy para personalizar o processo de serialização
Seafish
6
Nota lateral: não use usingcom HttpClient. Consulte: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
maxshuty
4
Com System.Net.Http.Formatting, você tem métodos de extensão definidos: "await httpClient.PostAsJsonAsync (" api / v1 / domain ", csObjRequest)"
hB0
13

Usando o pacote JSON.NET NuGet e tipos anônimos, você pode simplificar o que os outros pôsteres estão sugerindo:

// ...

string payload = JsonConvert.SerializeObject(new
{
    agent = new
    {
        name    = "Agent Name",
        version = 1,
    },

    username = "username",
    password = "password",
    token    = "xxxxx",
});

var client = new HttpClient();
var content = new StringContent(payload, Encoding.UTF8, "application/json");

HttpResponseMessage response = await client.PostAsync(uri, content);

// ...
Maximilian Burszley
fonte
6

Você pode construir seu HttpContentusando a combinação de JObjectpara evitar JPropertye, em seguida, chamá ToString()-lo ao construir o StringContent:

        /*{
          "agent": {                             
            "name": "Agent Name",                
            "version": 1                                                          
          },
          "username": "Username",                                   
          "password": "User Password",
          "token": "xxxxxx"
        }*/

        JObject payLoad = new JObject(
            new JProperty("agent", 
                new JObject(
                    new JProperty("name", "Agent Name"),
                    new JProperty("version", 1)
                    ),
                new JProperty("username", "Username"),
                new JProperty("password", "User Password"),
                new JProperty("token", "xxxxxx")    
                )
            );

        using (HttpClient client = new HttpClient())
        {
            var httpContent = new StringContent(payLoad.ToString(), Encoding.UTF8, "application/json");

            using (HttpResponseMessage response = await client.PostAsync(requestUri, httpContent))
            {
                response.EnsureSuccessStatusCode();
                string responseBody = await response.Content.ReadAsStringAsync();
                return JObject.Parse(responseBody);
            }
        }
Jan Dolejsi
fonte
Como você evita Exception while executing function. Newtonsoft.Json: Can not add Newtonsoft.Json.Linq.JProperty to Newtonsoft.Json.Linq.JArrayerros?
Jari Turkia de
1
Uma instância HttpClient não deve ser criada com o uso de construção. A instância deve ser criada uma vez e usada em todo o aplicativo. Isso ocorre porque ele usa seu próprio pool de conexão. Seu código tende principalmente a lançar SocketException. docs.microsoft.com/en-us/dotnet/api/…
Harun Diluka Heshan
2

Você também pode usar o método PostAsJsonAsync () disponível em HttpClient ()

   var requestObj= JsonConvert.SerializeObject(obj);
   HttpResponseMessage response = await    client.PostAsJsonAsync($"endpoint",requestObj).ConfigureAwait(false);

Rukshala Weerasinghe
fonte
1
Você pode adicionar uma explicação sobre o que o seu código faz e como ele resolve o problema?
Nilambar Sharma
Você pode pegar qualquer objeto que deseja postar e serializá-lo usando SerializeObject (); var obj= new Credentials { Agent = new Agent { Name = "Agent Name", Version = 1 }, Username = "Username", Password = "User Password", Token = "xxxxx" }; E então, sem ter que convertê-lo para httpContent, você pode usar PostAsJsonAsync () passando a URL do endpoint e o próprio objeto JSON convertido.
Rukshala Weerasinghe