A Recap of Request Handling in Go
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 FileServer
NotFoundHandler
e RedirectHandler
. Vamos começar com um exemplo simples, mas inventado.:
Vamos passo a passo através de rapidamente:
-
main
função usamos ohttp.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 parahttp://example.org
. - em seguida, usamos a função
mux.Handle
para 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çãohttp.ListenAndServe
, passando no nosso ServeMux para que ele corresponda aos pedidos.
Vá em frente e executar o aplicativo:
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:
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:
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:
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
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:
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:
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:
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:
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:
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.