Introdução: Por que é importante saber trabalhar com HTTP em Go
\nGo (Golang) é uma linguagem criada para trabalhar em rede. Sua biblioteca padrão net/http fornece ferramentas poderosas e flexíveis para criar tanto servidores quanto clientes. No entanto, muitos desenvolvedores, especialmente os vindos de linguagens de script, cometem erros típicos: esquecem de fechar o corpo da resposta, não configuram timeouts ou tratam erros incorretamente.
Neste artigo, vamos abordar a criação de um cliente HTTP em Go "de A a Z". Você aprenderá como enviar requisições GET e POST corretamente, gerenciar cabeçalhos, configurar timeouts e tratar erros. Todos os exemplos de código estão prontos para uso.
\n\n1. Fundamentos: criando um cliente e a primeira requisição GET
\nEm Go, o cliente HTTP é uma estrutura http.Client. Você pode usar o cliente global http.DefaultClient ou criar o seu próprio com configurações personalizadas. Vamos começar com uma requisição GET simples.
package main\n\nimport (\n "fmt"\n "io"\n "net/http"\n "os"\n)\n\nfunc main() {\n // Usando o cliente embutido\n resp, err := http.Get("https://api.example.com/data")\n if err != nil {\n fmt.Printf("Erro na requisição: %v\\n", err)\n os.Exit(1)\n }\n defer resp.Body.Close() // IMPORTANTE: sempre fechar o corpo da resposta!\n\n // Lendo o corpo da resposta\n body, err := io.ReadAll(resp.Body)\n if err != nil {\n fmt.Printf("Erro ao ler a resposta: %v\\n", err)\n os.Exit(1)\n }\n\n fmt.Printf("Status: %s\\n", resp.Status)\n fmt.Printf("Corpo: %s\\n", string(body))\n}\nPontos-chave:
\n- \n
- Sempre feche
resp.Bodyusandodefer. Caso contrário, ocorrerá vazamento de recursos (as conexões não retornarão ao pool). \n - Verifique o status da resposta (
resp.StatusCode). O código 200 não é o único de sucesso (existem 201, 204, etc.). \n io.ReadAlllê todo o corpo para a memória. Para respostas grandes, useio.Copyou processamento em fluxo. \n
2. Cliente avançado: timeouts, cabeçalhos e configurações personalizadas
\nO cliente global não possui timeouts por padrão — isso pode fazer o programa travar. Sempre crie seu próprio cliente com configurações.
\n2.1. Criando um cliente com timeout
\nclient := &http.Client{\n Timeout: 10 * time.Second, // Timeout total para toda a requisição\n}\n\nresp, err := client.Get("https://api.example.com/data")\nVocê também pode configurar timeouts em diferentes níveis através do Transport:
transport := &http.Transport{\n DialContext: (&net.Dialer{\n Timeout: 5 * time.Second, // Timeout de conexão\n KeepAlive: 30 * time.Second,\n }).DialContext,\n TLSHandshakeTimeout: 5 * time.Second,\n ResponseHeaderTimeout: 5 * time.Second,\n}\n\nclient := &http.Client{\n Transport: transport,\n Timeout: 15 * time.Second,\n}\n\n2.2. Adicionando cabeçalhos e parâmetros
\nPara requisições complexas, use http.NewRequest:
req, err := http.NewRequest("GET", "https://api.example.com/search", nil)\nif err != nil {\n log.Fatal(err)\n}\n\n// Adicionando cabeçalhos\nreq.Header.Set("Authorization", "Bearer your-token")\nreq.Header.Set("Accept", "application/json")\n\n// Adicionando parâmetros de consulta\nq := req.URL.Query()\nq.Add("query", "golang")\nq.Add("page", "1")\nreq.URL.RawQuery = q.Encode()\n\nresp, err := client.Do(req)\n\n3. Trabalhando com POST e outros métodos (JSON, formulários)
\nRequisições POST exigem o envio de um corpo. Vamos considerar dois formatos populares: JSON e formulário URL-encoded.
\n3.1. POST com JSON
\ntype RequestData struct {\n Name string `json:"name"`\n Email string `json:"email"`\n}\n\ndata := RequestData{\n Name: "João",\n Email: "joao@example.com",\n}\n\njsonData, err := json.Marshal(da