Interface http.Handler
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 77 de 107.
A interface http.Handler é a base da arquitetura do servidor web de Go. Ela define como as requisições HTTP são processadas, e qualquer tipo que a implemente pode atender a requisições web.
A interface é notavelmente simples:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}Qualquer tipo com um método ServeHTTP pode lidar com requisições HTTP. Aqui está um manipulador personalizado:
type GreetHandler struct {
Greeting string
}
func (h GreetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s, visitor!", h.Greeting)
}Você pode usar este handler diretamente com um servidor:
handler := GreetHandler{Greeting: "Welcome"}
http.Handle("/greet", handler)Para casos mais simples, o Go fornece o http.HandlerFunc, um tipo que permite converter uma função comum em um handler:
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!")
}
http.Handle("/hello", http.HandlerFunc(hello))O poder desta interface torna-se claro quando você percebe que handlers podem envolver outros handlers. Isso possibilita padrões de middleware onde você adiciona logging, autenticação ou outras preocupações transversais sem modificar a lógica original do handler.
Desafio
FácilVamos construir um sistema simples de manipulador HTTP que simula como as requisições web são processadas! Como não podemos executar um servidor HTTP real neste ambiente, você criará manipuladores que implementam a interface http.Handler e demonstrará como eles processam requisições trabalhando com objetos simulados (mock) de requisição/resposta.
Você organizará seu código em dois arquivos:
handlers.go: Defina seus manipuladores HTTP personalizados.Crie uma struct
MockResponseWriterque capture o que seria gravado em uma resposta HTTP. Ela deve armazenar o corpo da resposta como uma string e implementar um métodoWrite(data []byte) (int, error)que anexa dados ao corpo. Adicione um métodoBody() stringpara recuperar a resposta acumulada.Crie uma struct
MockRequestcom os campos de stringPatheMethodpara simular uma requisição HTTP.Agora crie duas structs de manipulador que implementem o padrão da interface
http.Handler(com um métodoServeHTTPque recebe um*MockResponseWritere um*MockRequest):WelcomeHandlercom um campoMessage- escreve a mensagem seguida pelo caminho da requisiçãoMethodHandler- escreve diferentes respostas com base no método da requisição:"Fetching data"para GET,"Creating resource"para POST, e"Method not supported"para qualquer outra coisa
main.go: Processe as requisições através dos seus manipuladores.Leia um tipo de manipulador (
welcomeoumethod) e, em seguida, leia os detalhes da requisição. Para o manipulador de boas-vindas, leia também a mensagem de boas-vindas.Crie o manipulador apropriado, construa uma requisição simulada e chame
ServeHTTPpara processá-la. Imprima o corpo da resposta.
As seguintes entradas serão fornecidas:
- Linha 1: Tipo de manipulador (
welcomeoumethod) - Linha 2: Caminho da requisição
- Linha 3: Método da requisição
- Linha 4 (apenas para welcome): Mensagem de boas-vindas
Por exemplo, dado:
welcome
/home
GET
Hello fromSua saída deve ser:
Hello from /homeE dado:
method
/api/users
POSTSua saída deve ser:
Creating resourceE dado:
method
/api/data
GETSua saída deve ser:
Fetching dataE dado:
method
/api/items
DELETESua saída deve ser:
Method not supportedEste desafio demonstra o conceito central da interface http.Handler—qualquer tipo com um método ServeHTTP pode manipular requisições. Em aplicações reais, o http.ResponseWriter e o *http.Request vêm da biblioteca padrão do Go, mas o padrão de implementação dos manipuladores permanece exatamente o mesmo.
Folha de consulta
A interface http.Handler é a base da arquitetura do servidor web de Go:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}Qualquer tipo com um método ServeHTTP pode processar requisições HTTP:
type GreetHandler struct {
Greeting string
}
func (h GreetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s, visitor!", h.Greeting)
}Registre um handler no servidor:
handler := GreetHandler{Greeting: "Welcome"}
http.Handle("/greet", handler)Para casos mais simples, use http.HandlerFunc para converter uma função em um handler:
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!")
}
http.Handle("/hello", http.HandlerFunc(hello))Handlers podem envolver outros handlers, permitindo padrões de middleware para logging, autenticação ou outras preocupações transversais.
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Ler o tipo de handler
scanner.Scan()
handlerType := scanner.Text()
// Ler o caminho da requisição
scanner.Scan()
path := scanner.Text()
// Ler o método da requisição
scanner.Scan()
method := scanner.Text()
// Criar requisição simulada (mock)
request := &MockRequest{
Path: path,
Method: method,
}
// Criar gravador de resposta simulado (mock)
response := &MockResponseWriter{}
// TODO: Com base no handlerType, criar o handler apropriado
// Se handlerType for "welcome", ler a mensagem de boas-vindas e criar WelcomeHandler
// Se handlerType for "method", criar MethodHandler
// Chamar ServeHTTP no handler com response e request
if handlerType == "welcome" {
scanner.Scan()
message := scanner.Text()
// TODO: Criar WelcomeHandler com a mensagem e chamar ServeHTTP
_ = message // Remova esta linha ao implementar
} else if handlerType == "method" {
// TODO: Criar MethodHandler e chamar ServeHTTP
}
// Imprimir o corpo da resposta
fmt.Println(response.Body())
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de OOP em Go
Arquivos ExternosGo Workspace e MódulosPacotes e ImportaçõesNomes Exportados vs Não ExportadosIntrodução a OOP em GoStructs como ClassesDefinindo Métodos em StructsRecebedores de Ponteiro vs ValorInicialização de StructsFunções ConstrutorasRecapitulação - Calculadora Simples4Interfaces
Introdução a InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Type AssertionType SwitchComposição de InterfacesInterfaces Stringer & ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs Não ExportadosEncapsulamento em Nível de PacoteMétodos Getter e SetterOcultação de Informação em GoRecapitulação - Registros de Estudantes10Generics (Go 1.18+)
Introdução a GenericsParâmetros de TipoRestrições de TipoStructs GenéricasSolução Alternativa para Métodos GenéricosRecapitulação - Coleção Genérica2Mergulho Profundo em Tipos e Structs
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosStruct TagsStructs AnônimasStructs AninhadasZero Values e PadrõesRecapitulação - Agenda de Contatos5Composição em vez de Herança
Por que Go não possui HerançaFundamentos de Incorporação de StructsPromoção de MétodosIncorporando Múltiplas StructsIncorporação vs AgregaçãoShadowing de Métodos IncorporadosRecapitulação - Hierarquia de Funcionários8Tratamento de Erros e OOP
A Interface errorTipos de Erro PersonalizadosError Wrapping (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - File Parser11Biblioteca Padrão & POO
io.Reader & io.Writersort.InterfaceInterface fmt.Stringerencoding/json com StructsInterface http.HandlerRecapitulação - Modelos de API REST3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs ReferênciaA Função new()Garbage Collection em GoRecapitulação - Construtor de Lista Encadeada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfaceColeções PolimórficasInjeção de DependênciaRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e ComunicaçãoCanais com Buffer vs. Sem BufferInstrução Selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de Structs Thread-SafeRecapitulação - Worker Pool