A Recap of Request Handling in Go

Last updated: 21st January 2018 Filed under: golang tutorial

Processing HTTP requests with Go is primarily about two things: ServeMuxes and Handlers.

Um ServeMux é essencialmente um roteador de requisição HTTP (ou multiplexor). Ele compara os pedidos recebidos com uma lista de caminhos de URL pré-definidos, e chama o tratador associado para o caminho sempre que uma correspondência é encontrada.os manipuladores são responsáveis por escrever os cabeçalhos e corpos de resposta. Quase qualquer objeto pode ser um manipulador, desde que satisfaça a interfacehttp.Handler. Em termos leigos, que simplesmente significa que ele deve ter uma ServeHTTP método com a seguinte assinatura:

ServeHTTP(http.ResponseWriter, *http.Request)

Vá a HTTP pacote vem com algumas funções para gerar comum manipuladores, como FileServerNotFoundHandler e RedirectHandler. Vamos começar com um exemplo simples, mas inventado.:

$ mkdir handler-example$ cd handler-example$ touch main.go

Arquivo: principais.ir

package mainimport ( "log" "net/http")func main() { mux := http.NewServeMux() rh := http.RedirectHandler("http://example.org", 307) mux.Handle("/foo", rh) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

Vamos passo a passo através de rapidamente:

  • main função usamos o http.NewServeMux função para criar um vazio ServeMux.
  • então usamos a função http.RedirectHandler para criar um novo manipulador. Este handler 307 redireciona todos os pedidos que recebe para http://example.org.
  • em seguida, usamos a função mux.Handlepara registrar isso com o nosso novo ServeMux, de modo que ele atua como o manipulador para todos os pedidos recebidos com o caminho URL/foo.finalmente criamos um novo servidor e começamos a ouvir os pedidos recebidos com a função http.ListenAndServe, passando no nosso ServeMux para que ele corresponda aos pedidos.

Vá em frente e executar o aplicativo:

$ go run main.goListening...

visite http://localhost:3000/foo no seu navegador. Você deve descobrir que seu pedido é redirecionado com sucesso.

The eagle-eyed of you might have noticed something interesting: The signature for the ListenAndServe function is ListenAndServe(addr string, handler Handler), but we passed a ServeMux as the second parameter.

Nós fomos capazes de fazer isso porque o tipo ServeMux também tem umServeHTTP método, o que significa que ele também satisfaz a interface de Handler.

para mim simplifica as coisas pensar em um ServeMux apenas como um tipo especial de manipulador, que em vez de fornecer uma resposta em si passa o pedido para um segundo manipulador. Isto não é tanto um salto como os primeiros sons – acorrentadores juntos é bastante comum em Go.

Personalizado Manipuladores

Vamos criar um manipulador personalizado que responde com a hora local atual em um determinado formato:

type timeHandler struct { format string}func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(th.format) w.Write(byte("The time is: " + tm))}

O código exato aqui não é muito importante.

Tudo o que realmente importa é que temos um objeto (neste caso, é uma timeHandler struct, mas pode igualmente ser uma seqüência de caracteres ou função, ou qualquer outra coisa), e temos implementado um método com a assinatura ServeHTTP(http.ResponseWriter, *http.Request) sobre ele. É tudo o que precisamos para fazer um encarregado.

vamos inserir isto num exemplo concreto:

File: main.ir

package mainimport ( "log" "net/http" "time")type timeHandler struct { format string}func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(th.format) w.Write(byte("The time is: " + tm))}func main() { mux := http.NewServeMux() th := &timeHandler{format: time.RFC1123} mux.Handle("/time", th) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

main função de nós inicializada timeHandler exatamente da mesma forma que seria de qualquer modo estrutura, usando o & símbolo para produzir um ponteiro. E então, como o exemplo anterior, usamos a função mux.Handle para registrar isso com nosso ServeMux.

Agora, quando executarmos a aplicação, o ServeMux irá passar qualquer pedido para /time straight on to our timeHandler.ServeHTTP method.vá em frente e tente: http://localhost:3000/time.

Observe, também, que poderíamos facilmente reutilizar o timeHandler em várias rotas:

func main() { mux := http.NewServeMux() th1123 := &timeHandler{format: time.RFC1123} mux.Handle("/time/rfc1123", th1123) th3339 := &timeHandler{format: time.RFC3339} mux.Handle("/time/rfc3339", th3339) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

Funções como Manipuladores

Para os casos mais simples (como o exemplo acima) com a definição de novos tipos personalizados e ServeHTTP métodos sente um pouco extenso. Vamos olhar para uma abordagem alternativa, onde nós alavancamos o ID

http.HandlerFunc

tipo para coagir uma função normal para satisfazer a interface de Handler.

qualquer função que tenha a assinatura func(http.ResponseWriter, *http.Request) pode ser convertida em um tipo HandlerFunc. Isto é útil porque os objetos HandleFunc vêm com um inbuilt ServeHTTP método que – bastante inteligente e convenientemente – executa o conteúdo da função original.

Se isso soa confuso, tente dar uma olhada no código fonte relevante. Você verá que é uma maneira muito sucinta de fazer uma função satisfazer a interface do manipulador.vamos reproduzir a aplicação timeHandler usando esta técnica:

File: main.ir

package mainimport ( "log" "net/http" "time")func timeHandler(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(time.RFC1123) w.Write(byte("The time is: " + tm))}func main() { mux := http.NewServeMux() // Convert the timeHandler function to a HandlerFunc type th := http.HandlerFunc(timeHandler) // And add it to the ServeMux mux.Handle("/time", th) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

De fato, a conversão de uma função para um HandlerFunc tipo e, em seguida, adicioná-lo a um ServeMux como isso é tão comum que Ir fornece um atalho: mux.HandleFunc método.

Isto é o que o main() função teria olhado como se tivéssemos usado este atalho:

func main() { mux := http.NewServeMux() mux.HandleFunc("/time", timeHandler) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

a Maior parte do tempo, usando uma função como um manipulador de como isso funciona bem. Mas há uma pequena limitação quando as coisas começam a ficar mais complexas.

Você provavelmente notou que, ao contrário do método anterior, nós tivemos que hardcode o formato de tempo na função timeHandler. O que acontece quando queremos passar informações ou variáveis de main() para um manipulador?

uma abordagem simples é colocar a nossa lógica de handler em um fechamento, e fechar sobre as variáveis que queremos usar:

File: main.ir

package mainimport ( "log" "net/http" "time")func timeHandler(format string) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write(byte("The time is: " + tm)) } return http.HandlerFunc(fn)}func main() { mux := http.NewServeMux() th := timeHandler(time.RFC1123) mux.Handle("/time", th) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

timeHandler função agora tem uma sutilmente diferente da função. Em vez de coagir a função a um manipulador (como fizemos anteriormente), estamos agora a usá-la para devolver um manipulador. Há dois elementos-chave para fazer isto funcionar.

Primeiro, ele cria fn, uma função anônima que acessa ‐ ou se fecha sobre – o format variável formando um fechamento. Independentemente do que fizermos com o fechamento, ele sempre será capaz de acessar as variáveis que são locais para o escopo em que foi criado – o que, neste caso, significa que ele sempre terá acesso à variávelformat.em segundo lugar, o nosso encerramento tem a assinatura func(http.ResponseWriter, *http.Request). Como você pode se lembrar de antes, isso significa que podemos convertê-lo em um tipo HandlerFunc (de modo que ele satisfaz a interface de Handler). A nossa função timeHandler devolve então este Fecho convertido.

neste exemplo, acabamos de passar uma cadeia simples para um manipulador. Mas em uma aplicação do mundo real você pode usar este método para passar a conexão de banco de dados, Mapa de modelo, ou qualquer outro contexto de nível de Aplicação. É uma boa alternativa ao uso de variáveis globais, e tem o benefício adicional de fazer tratadores auto-suficientes para testes.

Você também pode ver este mesmo padrão escrito como:

func timeHandler(format string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write(byte("The time is: " + tm)) })}

Ou usando uma conversão implícita para o HandlerFunc tipo de retorno:

The DefaultServeMux

provavelmente você viu DefaultServeMux mencionados em muitos lugares, desde os exemplos mais simples do mundo Hello até o código fonte Go.levei muito tempo a perceber que não é nada de especial. O DefaultServeMux é apenas um simples e velho ServeMux, como nós já temos usado, que se instancia por padrão quando o pacote HTTP é usado. Aqui está a linha relevante da fonte Go:

var DefaultServeMux = NewServeMux()

geralmente você não deve usar o DefaultServeMux porque ele representa um risco de segurança.

Devido a DefaultServeMux é armazenado em uma variável global, qualquer pacote é capaz de acessá-lo e registrar uma rota, incluindo quaisquer pacotes de terceiros que sua aplicação importa. Se um desses pacotes de terceiros for comprometido, eles podem usar o DefaultServeMux para expor um manipulador malicioso à web.

Por isso, como regra geral, é uma boa ideia evitar os DefaultServeMux, e em vez disso usar o seu próprio ServeMux com escopo local, como temos sido até agora. Mas se decidires usá-lo…

O pacote HTTP oferece um par de atalhos para trabalhar com o DefaultServeMux: http.Manípulo e http.HandleFunc. Estes fazem exatamente o mesmo que suas funções homônimas que já vimos, com a diferença que eles adicionam manipuladores para os Defaltservemux em vez de um que você criou.

adicionalmente, ListenAndServe irá voltar a usar o DefaultServeMux se não for fornecido outro manipulador (ou seja, o segundo parâmetro é definido como nil).

assim como um passo final, vamos atualizar a nossa aplicação timeHandler para usar o DefaultServeMux em vez:

File: main.go

package mainimport ( "log" "net/http" "time")func timeHandler(format string) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write(byte("The time is: " + tm)) } return http.HandlerFunc(fn)}func main() { // Note that we skip creating the ServeMux... var format string = time.RFC1123 th := timeHandler(format) // We use http.Handle instead of mux.Handle... http.Handle("/time", th) log.Println("Listening...") // And pass nil as the handler to ListenAndServe. http.ListenAndServe(":3000", nil)}

If you enjoyed this blog post, don’t forget to check out my new book about how to build professional web applications with Go!

Siga-me no Twitter @ajmedwards.

todos os excertos de código nesta publicação são livres de ser utilizados ao abrigo da licença do MIT.



Deixe uma resposta

O seu endereço de email não será publicado.