Programação HTTP
Lição 22: Programação HTTP
🎯 Analogia da Vida
Imagine que você administra um restaurante:
- Servidor HTTP é como o recepcionista do restaurante — quando os clientes chegam, o recepcionista os direciona para a janela de atendimento correta com base em suas necessidades
- Roteamento (ServeMux) é como as categorias do cardápio do restaurante — diferentes pratos (caminhos URL) são preparados por diferentes chefs (funções manipuladoras)
- Interface Handler é como os padrões de trabalho do chef — independentemente de qual prato está sendo preparado, eles devem seguir um fluxo de trabalho unificado
- Middleware é como o processo de serviço do restaurante — os clientes tiram os sapatos, lavam as mãos e depois se sentam; esses etapas são comuns a todos e não específicas de nenhum prato em particular
O pacote net/http do Go é seu "sistema de gerenciamento de restaurante", ajudando você a construir rapidamente um serviço web de alto desempenho e estável.
📚 Conceitos Centrais
| Conceito | Descrição |
|---|---|
http.Get/Post |
Enviar requisições HTTP para obter recursos remotos |
http.ListenAndServe |
Iniciar um servidor HTTP e escutar em uma porta |
Interface Handler |
A interface central que define os padrões de manipulação de requisições |
HandlerFunc |
Um adaptador que converte uma função regular em um Handler |
ServeMux |
Multiplexador de requisições HTTP (roteador) |
Middleware |
Um padrão para inserir lógica comum antes e depois da manipulação de requisições |
📝 Sintaxe Básica e Uso
1. Enviando Requisições HTTP
GO
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
// Enviar requisição GET
resp, err := http.Get("https://httpbin.org/get")
if err != nil {
fmt.Println("Requisição falhou:", err)
return
}
defer resp.Body.Close() // 💡 Deve fechar o corpo da resposta para evitar vazamentos de conexão
// Ler conteúdo da resposta
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Falha na leitura:", err)
return
}
fmt.Println("Código de status:", resp.StatusCode)
fmt.Println("Resposta:", string(body))
}
💡 Dica:
resp.Body deve ser fechado após o uso com Close(), caso contrário causará vazamentos de conexão. Usar defer é a melhor prática.
2. Enviando Requisições POST
GO
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
// Construir dados JSON
data := map[string]string{
"username": "gopher",
"email": "gopher@example.com",
}
jsonData, _ := json.Marshal(data)
// Enviar requisição POST
// 💡 O terceiro parâmetro é o corpo da requisição, requer tipo io.Reader
resp, err := http.Post(
"https://httpbin.org/post",
"application/json",
bytes.NewBuffer(jsonData),
)
if err != nil {
fmt.Println("Requisição falhou:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println("Código de status:", resp.StatusCode)
fmt.Println("Resposta:", string(body))
}
💡 Dica: O parâmetro Content-Type de
http.Post é muito importante — o servidor depende dele para analisar o formato do corpo da requisição.
3. Iniciando um Servidor HTTP
GO
package main
import (
"fmt"
"net/http"
)
func main() {
// Registrar manipuladores de rotas
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Bem-vindo à página inicial!")
})
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Olá, Gopher!")
})
// Iniciar servidor, escutar na porta 8080
// 💡 ListenAndServe bloqueia até o servidor ser desligado
fmt.Println("Servidor iniciado em http://localhost:8080")
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println("Servidor falhou ao iniciar:", err)
}
}
💡 Dica: Passar
nil como segundo parâmetro significa usar o DefaultServeMux padrão. Em produção, é recomendado criar um roteador personalizado.
4. Interface Handler
GO
// Definição da interface Handler: qualquer tipo que implemente o método ServeHTTP é um Handler
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
GO
package main
import (
"fmt"
"net/http"
)
// Tipo Handler personalizado
type GreetingHandler struct {
Message string
}
// Implementar o método ServeHTTP da interface Handler
func (g GreetingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, g.Message)
}
func main() {
// Usar Handler personalizado
handler := GreetingHandler{Message: "Olá, este é um Handler personalizado!"}
http.Handle("/greet", handler) // 💡 Nota: use Handle, não HandleFunc
http.ListenAndServe(":8080", nil)
}
💡 Dica:
Handle aceita uma interface Handler, HandleFunc aceita uma função. Ambos são funcionalmente equivalentes, apenas formas diferentes.
5. Adaptador HandlerFunc
GO
package main
import (
"fmt"
"net/http"
)
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Handler adaptado via HandlerFunc")
}
func main() {
// HandlerFunc converte uma função regular em um Handler
// 💡 Essencialmente uma conversão de tipo: type HandlerFunc func(ResponseWriter, *Request)
http.Handle("/adapted", http.HandlerFunc(myHandler))
// Forma equivalente (mais comumente usada)
http.HandleFunc("/simple", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Usar HandleFunc diretamente é mais conciso")
})
http.ListenAndServe(":8080", nil)
}
💡 Dica:
HandlerFunc é um adaptador de tipo que permite que funções regulares satisfaçam a interface Handler.
🧪 Exemplos Práticos
Exemplo: Servidor de Arquivos Estáticos Simples (Dificuldade ⭐)
GO
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
// Registrar serviço de arquivos estáticos usando o roteador padrão
// StripPrefix remove o prefixo "/static/" da URL
// FileServer fornece acesso aos arquivos no diretório
fs := http.FileServer(http.Dir("./public"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
// Manipulador da página inicial
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r) // 💡 Tratamento 404
return
}
fmt.Fprintf(w, "Bem-vindo ao meu site!")
})
fmt.Println("Servidor rodando em http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Execução:
BASH
# Criar diretório e arquivos de teste
mkdir -p public
echo "<h1>Olá</h1>" > public/index.html
# Executar o servidor
go run main.go
# Testar acesso (outro terminal)
curl http://localhost:8080/
curl http://localhost:8080/static/index.html
Exemplo: Roteamento Personalizado e Middleware (Dificuldade ⭐⭐)
GO
package main
import (
"fmt"
"log"
"net/http"
"time"
)
// Middleware de logging: registra o tempo de processamento de cada requisição
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
log.Printf("Início %s %s", r.Method, r.URL.Path)
next.ServeHTTP(w, r) // 💡 Chamar o próximo handler
log.Printf("Fim %s %s levou %v", r.Method, r.URL.Path, time.Since(start))
})
}
// Middleware de autenticação: verifica Token nos cabeçalhos da requisição
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "Acesso não autorizado", http.StatusUnauthorized)
return
}
log.Printf("Token do usuário: %s", token)
next.ServeHTTP(w, r)
})
}
func main() {
mux := http.NewServeMux() // 💡 Criar roteador personalizado
// Rotas públicas
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Bem-vindo ao serviço de API!")
})
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{"status": "ok"}`)
})
// Rotas que requerem autenticação
protected := http.NewServeMux()
protected.HandleFunc("/profile", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Esta é sua página de perfil")
})
protected.HandleFunc("/settings", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Esta é a página de configurações")
})
// 💡 Cadeia de middleware: logging primeiro, depois autenticação, depois manipulação
mux.Handle("/api/", AuthMiddleware(protected))
// Aplicar middleware de logging a todas as rotas
finalHandler := LoggingMiddleware(mux)
fmt.Println("Servidor rodando em http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", finalHandler))
}
Execução e Teste:
BASH
# Executar
go run main.go
# Testar rotas públicas
curl http://localhost:8080/
curl http://localhost:8080/health
# Testar rotas autenticadas (sem Token)
curl http://localhost:8080/api/profile
# Saída: Acesso não autorizado
# Testar rotas autenticadas (com Token)
curl -H "Authorization: Bearer mytoken123" http://localhost:8080/api/profile
# Saída: Esta é sua página de perfil
Exemplo: Serviço de API RESTful Completo (Dificuldade ⭐⭐⭐)
GO
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"strings"
"sync"
"time"
)
// Modelo User
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
// APIResponse formato de resposta unificado
type APIResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// UserStore armazenamento de usuários (usando map para simular banco de dados)
type UserStore struct {
mu sync.RWMutex
users map[int]*User
nextID int
}
func NewUserStore() *UserStore {
return &UserStore{
users: make(map[int]*User),
nextID: 1,
}
}
func (s *UserStore) Create(name, email string) *User {
s.mu.Lock()
defer s.mu.Unlock()
user := &User{
ID: s.nextID,
Name: name,
Email: email,
}
s.users[s.nextID] = user
s.nextID++
return user
}
func (s *UserStore) Get(id int) *User {
s.mu.RLock()
defer s.mu.RUnlock()
return s.users[id]
}
func (s *UserStore) List() []*User {
s.mu.RLock()
defer s.mu.RUnlock()
users := make([]*User, 0, len(s.users))
for _, u := range s.users {
users = append(users, u)
}
return users
}
func (s *UserStore) Update(id int, name, email string) *User {
s.mu.Lock()
defer s.mu.Unlock()
user, ok := s.users[id]
if !ok {
return nil
}
if name != "" {
user.Name = name
}
if email != "" {
user.Email = email
}
return user
}
func (s *UserStore) Delete(id int) bool {
s.mu.Lock()
defer s.mu.Unlock()
if _, ok := s.users[id]; !ok {
return false
}
delete(s.users, id)
return true
}
// UserHandler manipulador da API de usuários
type UserHandler struct {
store *UserStore
}
// writeJSON escreve uma resposta JSON
func (h *UserHandler) writeJSON(w http.ResponseWriter, code int, resp APIResponse) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
json.NewEncoder(w).Encode(resp)
}
// List obter lista de usuários GET /users
func (h *UserHandler) List(w http.ResponseWriter, r *http.Request) {
users := h.store.List()
h.writeJSON(w, http.StatusOK, APIResponse{
Code: 0,
Message: "success",
Data: users,
})
}
// Create criar usuário POST /users
func (h *UserHandler) Create(w http.ResponseWriter, r *http.Request) {
var req struct {
Name string `json:"name"`
Email string `json:"email"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.writeJSON(w, http.StatusBadRequest, APIResponse{
Code: 1,
Message: "Dados de requisição inválidos",
})
return
}
if req.Name == "" || req.Email == "" {
h.writeJSON(w, http.StatusBadRequest, APIResponse{
Code: 2,
Message: "Nome e email não podem ser vazios",
})
return
}
user := h.store.Create(req.Name, req.Email)
h.writeJSON(w, http.StatusCreated, APIResponse{
Code: 0,
Message: "Criado com sucesso",
Data: user,
})
}
// Get obter único usuário GET /users/{id}
func (h *UserHandler) Get(w http.ResponseWriter, r *http.Request) {
idStr := strings.TrimPrefix(r.URL.Path, "/users/")
id, err := strconv.Atoi(idStr)
if err != nil {
h.writeJSON(w, http.StatusBadRequest, APIResponse{
Code: 1,
Message: "ID de usuário inválido",
})
return
}
user := h.store.Get(id)
if user == nil {
h.writeJSON(w, http.StatusNotFound, APIResponse{
Code: 3,
Message: "Usuário não encontrado",
})
return
}
h.writeJSON(w, http.StatusOK, APIResponse{
Code: 0,
Message: "success",
Data: user,
})
}
// Update atualizar usuário PUT /users/{id}
func (h *UserHandler) Update(w http.ResponseWriter, r *http.Request) {
idStr := strings.TrimPrefix(r.URL.Path, "/users/")
id, err := strconv.Atoi(idStr)
if err != nil {
h.writeJSON(w, http.StatusBadRequest, APIResponse{
Code: 1,
Message: "ID de usuário inválido",
})
return
}
var req struct {
Name string `json:"name"`
Email string `json:"email"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.writeJSON(w, http.StatusBadRequest, APIResponse{
Code: 1,
Message: "Dados de requisição inválidos",
})
return
}
user := h.store.Update(id, req.Name, req.Email)
if user == nil {
h.writeJSON(w, http.StatusNotFound, APIResponse{
Code: 3,
Message: "Usuário não encontrado",
})
return
}
h.writeJSON(w, http.StatusOK, APIResponse{
Code: 0,
Message: "Atualizado com sucesso",
Data: user,
})
}
// Delete excluir usuário DELETE /users/{id}
func (h *UserHandler) Delete(w http.ResponseWriter, r *http.Request) {
idStr := strings.TrimPrefix(r.URL.Path, "/users/")
id, err := strconv.Atoi(idStr)
if err != nil {
h.writeJSON(w, http.StatusBadRequest, APIResponse{
Code: 1,
Message: "ID de usuário inválido",
})
return
}
if !h.store.Delete(id) {
h.writeJSON(w, http.StatusNotFound, APIResponse{
Code: 3,
Message: "Usuário não encontrado",
})
return
}
h.writeJSON(w, http.StatusOK, APIResponse{
Code: 0,
Message: "Excluído com sucesso",
})
}
// Middleware CORS
func CORSMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
// Middleware de recuperação: captura panics
func RecoveryMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("Panic capturado: %v", err)
http.Error(w, "Erro interno do servidor", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
func main() {
store := NewUserStore()
handler := &UserHandler{store: store}
// Criar roteador
mux := http.NewServeMux()
// 💡 Registrar rotas: despachar para diferentes manipuladores com base no método HTTP
mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
handler.List(w, r)
case http.MethodPost:
handler.Create(w, r)
default:
http.Error(w, "Método não permitido", http.StatusMethodNotAllowed)
}
})
// 💡 Manipular rotas com parâmetros de caminho /users/{id}
mux.HandleFunc("/users/", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
handler.Get(w, r)
case http.MethodPut:
handler.Update(w, r)
case http.MethodDelete:
handler.Delete(w, r)
default:
http.Error(w, "Método não permitido", http.StatusMethodNotAllowed)
}
})
// Aplicar cadeia de middleware
finalHandler := RecoveryMiddleware(CORSMiddleware(mux))
// Criar servidor (timeouts configuráveis)
server := &http.Server{
Addr: ":8080",
Handler: finalHandler,
ReadTimeout: 10 * time.Second, // 💡 Timeout de leitura
WriteTimeout: 10 * time.Second, // 💡 Timeout de escrita
}
// Inserir dados de teste
store.Create("Alice", "alice@example.com")
store.Create("Bob", "bob@example.com")
fmt.Println("Servidor da API RESTful rodando em http://localhost:8080")
fmt.Println("Endpoints disponíveis:")
fmt.Println(" GET /users - Obter lista de usuários")
fmt.Println(" POST /users - Criar usuário")
fmt.Println(" GET /users/{id} - Obter único usuário")
fmt.Println(" PUT /users/{id} - Atualizar usuário")
fmt.Println(" DELETE /users/{id} - Excluir usuário")
log.Fatal(server.ListenAndServe())
}
Execução e Teste Completo:
BASH
# Executar servidor
go run main.go
# Obter lista de usuários
curl http://localhost:8080/users
# Criar usuário
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"name": "Charlie", "email": "charlie@example.com"}'
# Obter único usuário
curl http://localhost:8080/users/1
# Atualizar usuário
curl -X PUT http://localhost:8080/users/1 \
-H "Content-Type: application/json" \
-d '{"name": "Alice Smith"}'
# Excluir usuário
curl -X DELETE http://localhost:8080/users/3
🎬 Cenários Práticos
Cenário 1: Construindo um Serviço de Consulta do Clima
GO
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
)
// WeatherResponse resposta simulada da API de clima
type WeatherResponse struct {
City string `json:"city"`
Temperature float64 `json:"temperature"`
Condition string `json:"condition"`
Humidity int `json:"humidity"`
}
// WeatherService manipulador do serviço de clima
type WeatherService struct {
data map[string]WeatherResponse
}
func NewWeatherService() *WeatherService {
return &WeatherService{
data: map[string]WeatherResponse{
"sao_paulo": {City: "São Paulo", Temperature: 28.5, Condition: "Ensolarado", Humidity: 45},
"rio": {City: "Rio de Janeiro", Temperature: 30.2, Condition: "Nublado", Humidity: 65},
"curitiba": {City: "Curitiba", Temperature: 18.1, Condition: "Tempestade", Humidity: 80},
},
}
}
// GetWeather consultar clima GET /weather?city=sao_paulo
func (s *WeatherService) GetWeather(w http.ResponseWriter, r *http.Request) {
// 💡 Obter nome da cidade dos parâmetros de consulta da URL
city := r.URL.Query().Get("city")
if city == "" {
http.Error(w, `{"error": "Por favor, forneça o parâmetro city"}`, http.StatusBadRequest)
return
}
city, _ = url.QueryUnescape(city)
weather, ok := s.data[city]
if !ok {
http.Error(w, fmt.Sprintf(`{"error": "Cidade não encontrada: %s"}`, city), http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(weather)
}
// GetForecast lista de previsões GET /forecast
func (s *WeatherService) GetForecast(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
forecasts := make([]WeatherResponse, 0, len(s.data))
for _, v := range s.data {
forecasts = append(forecasts, v)
}
json.NewEncoder(w).Encode(map[string]interface{}{
"count": len(forecasts),
"forecasts": forecasts,
})
}
func main() {
service := NewWeatherService()
mux := http.NewServeMux()
mux.HandleFunc("/weather", service.GetWeather)
mux.HandleFunc("/forecast", service.GetForecast)
fmt.Println("Serviço de clima rodando em http://localhost:8080")
fmt.Println("Uso:")
fmt.Println(" GET /weather?city=sao_paulo")
fmt.Println(" GET /forecast")
http.ListenAndServe(":8080", mux)
}
BASH
# Testar
curl "http://localhost:8080/weather?city=sao_paulo"
# {"city":"São Paulo","temperature":28.5,"condition":"Ensolarado","humidity":45}
curl http://localhost:8080/forecast
# {"count":3,"forecasts":[...]}
Cenário 2: Implementando Middleware de Limitação de Taxa
GO
package main
import (
"fmt"
"net/http"
"sync"
"time"
)
// RateLimiter limitador de taxa com balde de tokens
type RateLimiter struct {
mu sync.Mutex
tokens map[string]int
limit int
interval time.Duration
lastTick map[string]time.Time
}
func NewRateLimiter(limit int, interval time.Duration) *RateLimiter {
return &RateLimiter{
tokens: make(map[string]int),
limit: limit,
interval: interval,
lastTick: make(map[string]time.Time),
}
}
// Allow verifica se uma requisição é permitida
func (rl *RateLimiter) Allow(key string) bool {
rl.mu.Lock()
defer rl.mu.Unlock()
now := time.Now()
last, exists := rl.lastTick[key]
if !exists || now.Sub(last) >= rl.interval {
rl.tokens[key] = rl.limit
rl.lastTick[key] = now
}
if rl.tokens[key] <= 0 {
return false
}
rl.tokens[key]--
return true
}
// RateLimitMiddleware middleware de limitação de taxa
func RateLimitMiddleware(limiter *RateLimiter) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
clientIP := r.RemoteAddr
if !limiter.Allow(clientIP) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusTooManyRequests) // 429
fmt.Fprintf(w, `{"error": "Muitas requisições, tente novamente mais tarde"}`)
return
}
next.ServeHTTP(w, r)
})
}
}
func main() {
limiter := NewRateLimiter(10, time.Minute)
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Requisição bem-sucedida! Hora: %s", time.Now().Format("15:04:05"))
})
mux.HandleFunc("/api/data", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{"data": "Estes são dados da API", "hora": "%s"}`, time.Now().Format("15:04:05"))
})
handler := RateLimitMiddleware(limiter)(mux)
fmt.Println("Servidor de limitação de taxa rodando em http://localhost:8080")
fmt.Println("Regra de limite: máximo 10 requisições por minuto")
http.ListenAndServe(":8080", handler)
}
❓ Perguntas Frequentes
P1: Qual a diferença entre http.HandleFunc e http.Handle?
GO
// HandleFunc aceita uma função
http.HandleFunc("/path", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Olá")
})
// Handle aceita uma interface Handler
type MyHandler struct{}
func (h MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Olá")
}
http.Handle("/path", MyHandler{})
Diferença:
HandleFunc: Aceita uma função do tipofunc(ResponseWriter, *Request)Handle: Aceita um tipo que implementa a interfaceHandlerHandleFuncinternamente envolve a função em um adaptadorHandlerFunc
P2: Como obter parâmetros de requisição em uma função manipuladora?
GO
func handler(w http.ResponseWriter, r *http.Request) {
// 💡 Parâmetros de consulta (?key=value na URL)
name := r.URL.Query().Get("name")
// 💡 Dados do formulário (POST form-urlencoded)
r.ParseForm()
email := r.Form.Get("email")
// 💡 Parâmetros de caminho (necessário analisar manualmente ou usar roteador de terceiros)
path := r.URL.Path
// 💡 Cabeçalhos da requisição
token := r.Header.Get("Authorization")
// 💡 Corpo da requisição JSON
var data map[string]string
json.NewDecoder(r.Body).Decode(&data)
fmt.Fprintf(w, "name=%s, email=%s, token=%s", name, email, token)
}
P3: Como implementar desligamento gracioso do servidor?
GO
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
server := &http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Second) // Simular operação demorada
fmt.Fprintf(w, "Processamento concluído")
}),
}
// Iniciar servidor em uma goroutine
go func() {
fmt.Println("Servidor iniciado em :8080")
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("Erro no servidor: %v", err)
}
}()
// 💡 Escutar sinais do sistema
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit // Bloquear e aguardar sinal
fmt.Println("\nDesligando servidor graciosamente...")
// 💡 Dar 5 segundos para requisições existentes completarem
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatalf("Falha no desligamento: %v", err)
}
fmt.Println("Servidor desligado com segurança")
}
P4: Por que é recomendado passar um Mux personalizado como segundo parâmetro para ListenAndServe?
GO
// ❌ Usando DefaultServeMux padrão (não recomendado)
http.HandleFunc("/api/users", usersHandler)
http.ListenAndServe(":8080", nil)
// ✅ Usando Mux personalizado (recomendado)
mux := http.NewServeMux()
mux.HandleFunc("/api/users", usersHandler)
http.ListenAndServe(":8080", mux)
Razões:
DefaultServeMuxé global — qualquer pacote pode registrar rotas, o que pode causar conflitos- Mux personalizado tem escopo controlável, evitando substituições inesperadas de rotas
- Mais fácil de reutilizar e testar em diferentes instâncias de servidor
📖 Resumo
Esta lição cobriu o conteúdo central de programação HTTP em Go:
| Ponto de Conhecimento | Conclusões Principais |
|---|---|
| Cliente HTTP | http.Get/Post para enviar requisições, resp.Body deve ser fechado |
| Servidor HTTP | http.ListenAndServe para iniciar serviço, registro de rotas |
| Interface Handler | Método ServeHTTP(ResponseWriter, *Request) |
| HandlerFunc | Adaptador de função, converte função em Handler |
| ServeMux | Roteador, suporta correspondência de prefixo |
| Middleware | Envolvimento do Handler, insere lógica comum antes/depois das requisições |
| Desligamento Gracioso | Usar server.Shutdown e tratamento de sinais |
Padrão Central:
TEXT
Requisição do Cliente → Cadeia de Middleware → Correspondência de Rota → Manipulação do Handler → Retorno da Resposta
📝 Exercícios
Exercício 1: Implementar Serviço de Upload de Arquivo
Requisitos:
- Criar um endpoint
POST /uploadpara receber uploads de arquivos - Limitar tamanho do arquivo a 10MB
- Permitir apenas tipos de imagem (jpg, png, gif)
- Salvar arquivos no diretório
./uploads - Retornar a URL de acesso do arquivo
Exercício 2: Implementar Middleware de Autenticação JWT
Requisitos:
- Implementar geração e verificação simples de JWT (pode simular com Base64)
- Criar um endpoint
POST /loginpara obter um token - Criar um endpoint protegido
GET /protected - Passar token via
Authorization: Bearer <token>
Exercício 3: Construir um Limitador de Taxa de API Concorrentemente Seguro
Requisitos:
- Implementar algoritmo de limitação de taxa com janela deslizante
- Suportar configuração de máximo de requisições por janela e tamanho da janela
- Fornecer interface de middleware
- Usar Redis ou armazenamento em memória (em memória é suficiente)
📚 Próxima Lição
Parabéns por completar a programação HTTP! Na próxima lição, aprenderemos a Programação de Testes em Go — incluindo testes unitários, testes orientados por tabela, testes de benchmark e mais, ajudando você a escrever código mais confiável.



