Como postar JSON em um servidor usando c #?

269

Aqui está o código que estou usando:

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

Quando estou executando isso, sempre recebo 500 erros internos do servidor.

O que estou fazendo de errado?

Arsen Zahray
fonte
1
Primeiro, verifique se os dados que você publica são o que o servidor espera.
LB
na verdade, parece que eu estava postando dados inválidos ...
Arsen Zahray
Para facilitar o trabalho, você pode adicionar JSON biblioteca com o visual studio também
Alireza Tabatabaeian
@Arsen - O servidor não deve falhar com dados malformados. Arquive um relatório de bug.
jww 9/09/19

Respostas:

396

A maneira como faço e trabalho é:

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

Eu escrevi uma biblioteca para executar esta tarefa de uma maneira mais simples, está aqui: https://github.com/ademargomes/JsonRequest

Espero que ajude.

Ademar
fonte
3
Eu acho que a linha de string json deve ser: string json = "{\" user \ ": \" test \ "," + "\" password \ ": \" bla \ "}"; Parece que você está perdendo um \
Dream Lane
3
Sempre use "application / json" (a menos que por algum outro motivo seja necessário texto / json, por exemplo: entwicklungsgedanken.de/2008/06/06/06 ). A criação vai para: stackoverflow.com/questions/477816/… .
Yaniv
34
Eu teria pensado o streamWriter.Flush (); e streamWriter.Close (); não é necessário, pois você está dentro de um bloco de uso. No final do bloco using, o gravador de fluxo será fechado de qualquer maneira.
Ruchira 19/01
1
Não crie JSON manualmente. É fácil cometer erros que permitem a injeção de JSON.
Florian Winter
5
@ user3772108 Consulte stackoverflow.com/a/16380064/2279059 . Use uma biblioteca JSON, como Newtonsoft JSON.Net, e processe a cadeia JSON de um objeto ou use serialização. Entendo que isso foi omitido aqui por simplicidade (embora o ganho de simplicidade seja mínimo), mas a formatação de cadeias de dados estruturadas (JSON, XML, ...) é muito perigosa para fazer isso, mesmo em cenários triviais, e incentivar as pessoas a copiar esse código .
Florian Winter
149

A solução de Ademar pode ser aprimorada usando JavaScriptSerializero Serializemétodo de alavancagem para fornecer conversão implícita do objeto em JSON.

Além disso, é possível aproveitar a usingfuncionalidade padrão da instrução para omitir explicitamente a chamada Flushe 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
fonte
1
Qual é a diferença entre este e o código acima, estou faltando alguma coisa?
precisa
16
Isso usa o método Serialize do JavaScriptSerializer para criar JSON válido em vez de criá-lo manualmente.
Sean Anderson
Veja a resposta de Jean F abaixo - deve ser um comentário. Tome cuidado com o tipo de conteúdo application/jsonestá correto.
Lucas
@SeanAnderson Eu continuo tendo o erro "Não foi possível conectar ao servidor remoto".
Ralphgabb
3
@LuzanBaral você só precisa de uma montagem: System.Web.Extensions
Norbrecht
60

O HttpClienttipo é uma implementação mais recente que o WebCliente HttpWebRequest.

Você pode simplesmente usar as seguintes linhas.

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

insira a descrição da imagem aqui

Quando você precisar HttpClientmais de uma vez, é recomendável criar apenas uma instância e reutilizá-la ou usar a nova HttpClientFactory.

NtFreX
fonte
5
Uma pequena observação sobre o HttpClient, o consenso geral é que você não deve descartá-lo. Mesmo implementando IDisposable, o objeto é seguro para threads e deve ser reutilizado. stackoverflow.com/questions/15705092/…
Jean F.
1
@JeanF. Ei, obrigado pela entrada. Como já observei, você deve criar apenas uma instância ou usar o HttpClientFactory. Não li todas as respostas no problema vinculado, mas acho que ele precisa ser atualizado, pois não menciona a fábrica.
NtFreX 15/1118
33

Após a publicação de Sean, não é necessário aninhar as instruções using. Pelo usingStreamWriter, ele será liberado e fechado no final do bloco, portanto, não é necessário chamar explicitamente os métodos Flush()e 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
fonte
1
agora essa resposta e a resposta de Sean Anderson são exatamente as mesmas, pois Sean editou seu post.
precisa saber é
Ei, isso é ótimo. Obrigado. Mas como vamos passar dados se tivermos nós filhos no nosso json?
user2728409
1
O serializador pode manipular nós filhos no json - você apenas precisa fornecer um objeto json válido.
David Clarke
14

Se você precisar chamar de forma assíncrona, use

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
fonte
3
Obrigado por postar esta solução Vivek. Em nosso cenário, tentamos outra solução neste post e acabamos vendo exceções de System.Threading em nosso aplicativo, devido ao que eu suponho que eram postagens síncronas bloqueando threads. Seu código resolveu nosso problema.
Ken Palmer
Observe que você provavelmente não precisa converter para bytes. Você deve poder fazer postStream.Write(postData);- e, dependendo da API, pode precisar usar um em request.ContentType = "application/json";vez de text/json.
vapcguy 22/04
13

Cuide do tipo de conteúdo que você está usando:

application/json

Fontes :

RFC4627

Outro post

Jean F.
fonte
11

Recentemente, criei uma maneira muito mais simples de postar um JSON, com a etapa adicional de converter de um modelo no meu aplicativo. Observe que você precisa criar o modelo [JsonObject] para seu controlador para obter os valores e fazer a conversão.

Solicitação:

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

Modelo:

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

Lado do servidor:

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

Esta opção não é mencionada:

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
fonte
2
Esta opção não está mais disponível desde o .NET 4.5.2. veja aqui stackoverflow.com/a/40525794/2161568
Downhillski
Votos negativos pelo comentário acima - como isso não está disponível, provavelmente deve remover a resposta.
NovaDev 03/07
1
Essa não é uma boa razão para reduzir a votação desta resposta, pois nem todo mundo usa as versões mais recentes do .net e, portanto, essa é uma resposta válida.
Ellisan
4

Uma maneira diferente e limpa de conseguir isso é usando o HttpClient assim:

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
fonte
4
Útil, no entanto, PostAsJsonAsyncnão está mais disponível desde o .NET 4.5.2. Use em PostAsyncvez disso. Mais aqui
Zachary Keener
HttpClient geralmente não deve ser usado em uma usingdeclaração como esta
p3tch
Acho que implementa IDisposableinterface para uma razão
Dima Daron
4

AVISO! Eu tenho uma visão muito forte sobre esse assunto.

Os clientes da Web existentes do .NET não são compatíveis com o desenvolvedor! WebRequest e WebClient são exemplos principais de "como frustrar um desenvolvedor". Eles são detalhados e complicados de se trabalhar; quando tudo o que você deseja fazer é uma simples solicitação de postagem em c #. O HttpClient ajuda de alguma maneira a resolver esses problemas, mas ainda fica aquém. Além disso, a documentação da Microsoft é ruim ... muito ruim; a menos que você queira vasculhar páginas e páginas de resumo técnico.

Código aberto para o resgate. Existem três excelentes bibliotecas NuGet gratuitas de código aberto como alternativas. Obrigado Senhor! Tudo isso é bem suportado, documentado e, sim, fácil - correção ... super fácil - de se trabalhar.

Não há muito entre eles, mas eu daria ao ServiceStack.Text a ligeira vantagem…

  • As estrelas do Github são praticamente as mesmas.
  • Questões abertas e, mais importante, com que rapidez todas as questões foram encerradas? O ServiceStack recebe o prêmio aqui pela resolução de problemas mais rápida e sem problemas em aberto.
  • Documentação? Todos têm ótima documentação; no entanto, o ServiceStack o eleva para o próximo nível e é conhecido por seu 'padrão de ouro' para documentação.

Ok - então, como é uma Solicitação de postagem em JSON no ServiceStack.Text?

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

Essa é uma linha de código. Conciso e fácil! Compare o acima com as bibliotecas Http do .NET.

Programando com Mark
fonte
3

Finalmente invoquei no modo de sincronização incluindo o .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
fonte
1

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

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

Use ASCII em vez de UFT8

user3280472
fonte
2
parece uma péssima idéia, estou perdendo alguma coisa?
precisa saber é o seguinte
JSON pode conter caracteres UTF8, isso parece uma péssima idéia.
Adrian Smith