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?


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.


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

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.


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

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

    }

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


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

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

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

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.


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

1

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

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

Use ASCII em vez de UFT8


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.