Como fazer uma solicitação HTTP GET corretamente

112

Ainda sou novo no c # e estou tentando criar um aplicativo para esta página que me avisará quando receber uma notificação (respondida, comentada, etc.). Mas, por enquanto, estou apenas tentando fazer uma chamada simples para a API que obterá os dados do usuário.

Estou usando o Visual studio express 2012 para construir o aplicativo C #, onde (por enquanto) você insere seu id de usuário, para que o aplicativo faça a solicitação com o id de usuário e mostre as estatísticas deste id de usuário.

aqui está o código em que estou tentando fazer a solicitação:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//Request library
using System.Net;
using System.IO;

namespace TestApplication
{
    class Connect
    {
        public string id;
        public string type;

        protected string api = "https://api.stackexchange.com/2.2/";
        protected string options = "?order=desc&sort=name&site=stackoverflow";

        public string request()
        {
            string totalUrl = this.join(id);

            return this.HttpGet(totalUrl);
        }

        protected string join(string s)
        {
            return api + type + "/" + s + options;
        }

        protected string get(string url)
        {
            try
            {
                string rt;

                WebRequest request = WebRequest.Create(url);

                WebResponse response = request.GetResponse();

                Stream dataStream = response.GetResponseStream();

                StreamReader reader = new StreamReader(dataStream);

                rt = reader.ReadToEnd();

                Console.WriteLine(rt);

                reader.Close();
                response.Close();

                return rt;
            }

            catch(Exception ex)
            {
                return "Error: " + ex.Message;
            }
        }
        public string HttpGet(string URI)
        {
            WebClient client = new WebClient();

            // Add a user agent header in case the 
            // requested URI contains a query.

            client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");

            Stream data = client.OpenRead(URI);
            StreamReader reader = new StreamReader(data);
            string s = reader.ReadToEnd();
            data.Close();
            reader.Close();

            return s;
        }
    }
}

a classe é um objeto e está sendo acessada a partir do formulário apenas analisando o ID do usuário e fazendo a solicitação.

Tentei muitos dos exemplos que procurei no google, mas não faço ideia por que estou recebendo de todas as formas a mensagem " ".

Eu sou novo neste tipo de algoritmo, se alguém puder compartilhar um livro ou tutorial que mostra como fazer esse tipo de coisa (explicando cada etapa), eu agradeceria

Oscar Reyes
fonte

Respostas:

247

Os servidores às vezes compactam suas respostas para economizar largura de banda; quando isso acontece, você precisa descompactar a resposta antes de tentar lê-la. Felizmente, o .NET framework pode fazer isso automaticamente, no entanto, temos que ativar a configuração.

Aqui está um exemplo de como você pode conseguir isso.

string html = string.Empty;
string url = @"https://api.stackexchange.com/2.2/answers?order=desc&sort=activity&site=stackoverflow";

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.AutomaticDecompression = DecompressionMethods.GZip;

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
    html = reader.ReadToEnd();
}

Console.WriteLine(html);

OBTER

public string Get(string uri)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

    using(HttpWebResponse response = (HttpWebResponse)request.GetResponse())
    using(Stream stream = response.GetResponseStream())
    using(StreamReader reader = new StreamReader(stream))
    {
        return reader.ReadToEnd();
    }
}

GET assíncrono

public async Task<string> GetAsync(string uri)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

    using(HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
    using(Stream stream = response.GetResponseStream())
    using(StreamReader reader = new StreamReader(stream))
    {
        return await reader.ReadToEndAsync();
    }
}

POST
Contém o parâmetro methodno caso de você desejar usar outros métodos HTTP, como PUT, DELETE, ETC

public string Post(string uri, string data, string contentType, string method = "POST")
{
    byte[] dataBytes = Encoding.UTF8.GetBytes(data);

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
    request.ContentLength = dataBytes.Length;
    request.ContentType = contentType;
    request.Method = method;

    using(Stream requestBody = request.GetRequestStream())
    {
        requestBody.Write(dataBytes, 0, dataBytes.Length);
    }

    using(HttpWebResponse response = (HttpWebResponse)request.GetResponse())
    using(Stream stream = response.GetResponseStream())
    using(StreamReader reader = new StreamReader(stream))
    {
        return reader.ReadToEnd();
    }
}

    

POST async
Contém o parâmetro methodcaso você deseje usar outros métodos HTTP, como PUT, DELETE, ETC

public async Task<string> PostAsync(string uri, string data, string contentType, string method = "POST")
{
    byte[] dataBytes = Encoding.UTF8.GetBytes(data);

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
    request.ContentLength = dataBytes.Length;
    request.ContentType = contentType;
    request.Method = method;

    using(Stream requestBody = request.GetRequestStream())
    {
        await requestBody.WriteAsync(dataBytes, 0, dataBytes.Length);
    }

    using(HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
    using(Stream stream = response.GetResponseStream())
    using(StreamReader reader = new StreamReader(stream))
    {
        return await reader.ReadToEndAsync();
    }
}
Aydin
fonte
4
Você pode querer mostrar um exemplo de como analisar a htmlstring +1para código limpo a propósito ..
MethodMan
obrigado, não sabia sobre a descompactação, sou desenvolvedor php / nodejs e esta é a primeira vez que começo a desenvolver aplicativos para desktop.
Oscar Reyes
Bem-vindo, você pode querer dar uma olhada em 'Newtonsoft.Json' para desserializar a resposta JSON que você recupera.
Aydin
há alguma chance de versão assíncrona
ahmad molaie
2
@ahmadmolaie Adicionados, bem como como fazer solicitações POST
Aydin
39

Outra maneira é usar 'HttpClient' assim:

using System;
using System.Net;
using System.Net.Http;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Making API Call...");
            using (var client = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }))
            {
                client.BaseAddress = new Uri("https://api.stackexchange.com/2.2/");
                HttpResponseMessage response = client.GetAsync("answers?order=desc&sort=activity&site=stackoverflow").Result;
                response.EnsureSuccessStatusCode();
                string result = response.Content.ReadAsStringAsync().Result;
                Console.WriteLine("Result: " + result);
            }
            Console.ReadLine();
        }
    }
}

HttpClient vs HttpWebRequest

Atualização de 22 de junho de 2020: não é recomendado usar httpclient em um bloco 'usando', pois isso pode causar o esgotamento da porta.

private static HttpClient client = null;
    
ContructorMethod()
{
   if(client == null)
   {
        HttpClientHandler handler = new HttpClientHandler()
        {
            AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
        };        
        client = new HttpClient(handler);
   }
   client.BaseAddress = new Uri("https://api.stackexchange.com/2.2/");
   HttpResponseMessage response = client.GetAsync("answers?order=desc&sort=activity&site=stackoverflow").Result;
   response.EnsureSuccessStatusCode();
   string result = response.Content.ReadAsStringAsync().Result;
            Console.WriteLine("Result: " + result);           
 }

Se estiver usando .Net Core 2.1+, considere usar IHttpClientFactory e injetar assim em seu código de inicialização.

 var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
            TimeSpan.FromSeconds(60));

 services.AddHttpClient<XApiClient>().ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
        {
            AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
        }).AddPolicyHandler(request => timeout);
ESCAVAÇÃO
fonte
1
Obrigado! Muito útil para mim. Eu apenas modifiquei um pouco, incluindo a resposta e o conteúdo na instrução "usando":
codely
5
Por aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong , nunca envolva o HttpClient em uma instrução using.
sfors diz restabelecer Monica em
4
@sfors Nunca diga nunca. Olhe o código. A HttpClientinstância é usada exatamente uma vez durante a vida útil do programa e é descartada pouco antes de o programa ser encerrado. Isso é completamente correto e apropriado.
Todd Menier de
Não sei como você pode contestar esse artigo e outros sobre como criar corretamente uma instância do HttpClient. Usando uma variável estática privada, que não é descartada. Por causa disso, conforme citado naquele artigo: (com relação a não usar dispose) ... "Mas HttpClient é diferente. Embora implemente a interface IDisposable, na verdade é um objeto compartilhado. Isso significa que, por baixo do pano, é reentrante) e thread seguro. Em vez de criar uma nova instância de HttpClient para cada execução, você deve compartilhar uma única instância de HttpClient para toda a vida útil do aplicativo. "
sfors diz restabelecer Monica em
Percebo que meu comentário está 2 anos atrasado, mas Todd não contestou o artigo. Todd estava simplesmente dizendo que, dado o exemplo de programa completo, um único HttpClient é usado durante a vida útil do aplicativo.
John
4

Maneira mais simples para minha opinião

  var web = new WebClient();
  var url = $"{hostname}/LoadDataSync?systemID={systemId}";
  var responseString = web.DownloadString(url);

OU

 var bytes = web.DownloadData(url);
Сергей Кислинский
fonte
3
var request = (HttpWebRequest)WebRequest.Create("sendrequesturl");
var response = (HttpWebResponse)request.GetResponse();
string responseString;
using (var stream = response.GetResponseStream())
{
    using (var reader = new StreamReader(stream))
    {
        responseString = reader.ReadToEnd();
    }
}
Manish sharma
fonte
5
o código não descarta objetos; pode ser um vazamento de memória. Precisa usar declarações.
StarTrekRedneck
Você não pode atribuir <null> a uma variável digitada implicitamente!
Luca Ziegler
É apenas declarar null.i saber que.i remover null.
Manish sharma