chi: Roteamento Leve e Middleware
O chi adiciona roteamento ciente de métodos, parâmetros de URL e composição de middleware sobre o net/http sem introduzir um tipo de handler personalizado.
Busque em todas as páginas da documentação
O chi adiciona roteamento ciente de métodos, parâmetros de URL e composição de middleware sobre o net/http sem introduzir um tipo de handler personalizado.
Handlers continuam sendo func(http.ResponseWriter, *http.Request), então o chi se encaixa em equipes que desejam a ergonomia de um roteador com portabilidade do stdlib.
O chi constrói uma árvore de radix de rotas no momento do registro e despacha as requisições recebidas através de uma pilha de middleware antes que seu handler seja executado.
Grupos de rotas (Route, Group, Mount) permitem que você escopo middleware e prefixos para subárvores.
Como chi.Router implementa http.Handler, você o insere em http.Server, testes de integração e proxies reversos da mesma forma que faria com um ServeMux simples.
Cartão de receita de referência rápida - pronto para copiar e colar.
r := chi.NewRouter()
r.Use(middleware.RequestID, middleware.Recoverer)
r.Route("/api", func(r chi.Router) {
r.Get("/users/{id}", getUser)
})
http.ListenAndServe(":8080", r)Quando usar isso:
http.Handler do stdlib (OTel, gateways de autenticação)http.Handler para os consumidores montaremhttp.ServeMux mantendo os handlers existentespackage main
import (
"encoding/json"
"net/http"
"strconv"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
type user struct {
ID int `json:"id"`
Name string `json:"name"`
}
var users = map[int]user{1: {ID: 1, Name: "Ada"}}
func getUser(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "invalid id", http.StatusBadRequest)
return
}
u, ok := users[id]
if !ok {
http.Error(w, "not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(u)
}
func main() {
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(30 * time.Second))
r.Route("/api/v1", func(r chi.Router) {
r.Get("/users/{id}", getUser)
r.Post("/users", func(w http.ResponseWriter, r *http.Request) {
var in user
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
users[in.ID] = in
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(in)
})
})
srv := &http.Server{Addr: ":8080", Handler: r}
srv.ListenAndServe()
}O que isso demonstra:
/api/v1chi.NewRouter() aloca uma árvore de rotas; cada Get/Post registra um nó de método + padrão.Use envolve a cadeia de handlers do roteador atual; sub-roteadores herdam o middleware pai, a menos que sejam registrados antes de um branch.Mount("/prefix", sub) remove o prefixo e delega a correspondência para sub.http.NotFound, a menos que você defina r.NotFound e r.MethodNotAllowed.| Padrão | Corresponde | Acesso ao Parâmetro |
|---|---|---|
/users/{id} | /users/42 | chi.URLParam(r, "id") |
/files/{path:*} | /files/a/b/c | segmento rest "greedy" |
/health | caminho exato | nenhum |
O middleware externo é executado primeiro na entrada e por último na saída.
Registre logging e recuperação cedo; coloque a autenticação após o logging para que as requisições rejeitadas ainda deixem um rastro de auditoria.
O middleware de timeout deve ficar fora dos handlers que podem bloquear.
import "context"
// Middleware personalizado: anexa um valor ao contexto, estilo stdlib
func withTenant(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tenant := r.Header.Get("X-Tenant")
type tenantKeyType struct{}
ctx := context.WithValue(r.Context(), tenantKeyType{}, tenant)
next.ServeHTTP(w, r.WithContext(ctx))
})
}Prefira chaves de contexto tipadas (tipo int personalizado) em vez de strings nuas para evitar colisões.
r.URL.Path bruto confunde os operadores. Correção: registre chi.RouteContext(r).RoutePattern ou inclua o prefixo de montagem em campos estruturados.Use após Mount não envolve retroativamente subárvores montadas registradas anteriormente. Correção: registre Use compartilhado no pai antes de Mount, ou adicione middleware em cada sub-roteador.chi.URLParam retorna string; esquecer strconv produz IDs 0 silenciosos. Correção: analise e valide antes das chamadas de domínio.http.ListenAndServe usa timeouts zero; o middleware Timeout do chi ajuda, mas não substitui ReadHeaderTimeout em http.Server. Correção: configure os timeouts de http.Server explicitamente./users para /users/ dependendo do registro; clientes inconsistentes podem dar requisições duplas. Correção: escolha um estilo e registre ambos ou desative os redirecionamentos conscientemente.http.HandlerFunc.| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
stdlib ServeMux (1.22+) | Dependências mínimas, padrões simples | Você precisa de grupos de rotas e pilhas de middleware ricas |
| gorilla/mux | Correspondência de Regex/host/header | Você quer velocidade de recursos ativa e desempenho de radix |
| Gin/Echo | Vinculação JSON pesada e middleware de framework | Você exige assinaturas http.Handler puras em todos os lugares |
httprouter diretamente | Velocidade bruta máxima, API minúscula | Você quer middleware agrupado e grupos de rotas |
Sim - muitas APIs de produção usam chi como uma camada fina sobre net/http com configuração explícita do servidor e middleware de observabilidade.
Construa um roteador, chame rr := httptest.NewRecorder(), req := httptest.NewRequest("GET", "/api/v1/users/1", nil), então r.ServeHTTP(rr, req).
Use http.FileServer envolvido com StripPrefix, ou middleware.Compress mais FileServer montado em um sub-roteador.
Escreva um pequeno helper que define Content-Type, status e codifica uma struct; evite espalhar http.Error com texto puro em APIs JSON.
Use o middleware github.com/go-chi/cors ou seu próprio handler Access-Control-* no início da cadeia.
O chi não gera especificações automaticamente; mantenha o OpenAPI separadamente ou use ferramentas de codegen que aceitam tabelas de rotas que você documenta.
Route e Group criam sub-roteadores que herdam o middleware Use pai registrado antes do bloco do grupo.
Monte o http.Handler do gateway com r.Mount e mantenha o gRPC em um listener separado ou use cmux com propriedade clara.
O chi permanece ativamente mantido, usa uma árvore de radix rápida e corresponde idiomaticamente aos padrões HTTP modernos do Go.
middleware.RequestID define o cabeçalho; leia-o do contexto ou de wrappers de gravador de resposta no middleware downstream.
Sim - passe o roteador como Handler; configure TLS em http.Server como de costume.
Defina r.MethodNotAllowed para um handler personalizado; o comportamento padrão retorna 405 quando o caminho corresponde, mas o verbo não.
Versões de Stack: Esta página foi escrita para Go 1.26.x (padrão GC Green Tea, go fix modernizers - verifique o patch na compilação), chi (última versão - verifique na compilação), gin (última versão - verifique na compilação), echo (última versão - verifique na compilação), google.golang.org/grpc (última versão - verifique na compilação), sigs.k8s.io/controller-runtime (última versão - verifique na compilação), kubebuilder (última versão - verifique na compilação), tinygo (última versão - verifique os alvos de placa na compilação), wazero (última versão - verifique na compilação) e golangci-lint (última versão - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026