
Há cinco anos, comecei a
desenvolver Gophish , o que tornou possível aprender Golang. Percebi que o Go é uma linguagem poderosa, cujos recursos são complementados por muitas bibliotecas. O Go é universal: em particular, com sua ajuda, é possível desenvolver aplicativos de servidor sem problemas.
Este artigo é sobre como escrever um servidor no Go. Vamos começar com coisas simples, como "Olá, mundo!", E terminar com um aplicativo com os seguintes recursos:
- Usando o Let's Encrypt for HTTPS.
- Trabalhe como um roteador API.
- Trabalhe com middleware.
- Processamento de arquivos estáticos.
- Desligamento correto.
A Skillbox recomenda: O desenvolvedor Python desde o início do curso prático .
Lembramos que: para todos os leitores de "Habr" - um desconto de 10.000 rublos ao se inscrever em qualquer curso Skillbox usando o código promocional "Habr".
Olá mundo!
Criar um servidor Web no Go é muito rápido. Aqui está um exemplo de uso de um manipulador que retorna o "Olá, mundo!" Prometido acima.
package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) http.ListenAndServe(":80", nil) }
Depois disso, se você iniciar o aplicativo e abrir a página
localhost , verá imediatamente o texto "Olá, mundo!" (é claro, se tudo funcionar corretamente).
Em seguida, usaremos repetidamente o manipulador, mas primeiro vamos entender como tudo funciona.
net / http
O exemplo usou o pacote
net/http
, que é a principal ferramenta do Go para desenvolver servidores e clientes HTTP. Para entender o código, vejamos o significado de três elementos importantes: http.Handler, http.ServeMux e http.Server.
Manipuladores HTTP
Quando recebemos uma solicitação, o manipulador analisa e forma uma resposta. Os manipuladores Go são implementados da seguinte maneira:
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
O primeiro exemplo usa a função auxiliar http.HandleFunc. Ele envolve outra função, que por sua vez aceita http.ResponseWriter e http.Request no ServeHTTP.
Em outras palavras, os manipuladores Golang são representados por uma única interface, que oferece muitas oportunidades para o programador. Assim, por exemplo, o middleware é implementado usando um manipulador, onde o ServeHTTP primeiro faz alguma coisa e depois chama o método ServeHTTP de outro manipulador.
Como mencionado acima, os manipuladores simplesmente geram respostas às solicitações. Mas qual manipulador específico deve ser usado em um determinado momento?
Solicitar roteamento
Para fazer a escolha certa, use o multiplexador HTTP. É chamado muxer ou roteador em várias bibliotecas, mas é tudo a mesma coisa. A função do multiplexador é analisar o caminho da solicitação e selecionar o manipulador apropriado.
Se você precisar de suporte para roteamento complexo, é melhor usar bibliotecas de terceiros. Uma das mais avançadas é
gorilla / mux e
go-chi / chi , essas bibliotecas possibilitam implementar o processamento intermediário sem problemas. Com a ajuda deles, você pode configurar o roteamento de caracteres curinga e executar várias outras tarefas. Sua vantagem é a compatibilidade com os manipuladores HTTP padrão. Como resultado, você pode escrever um código simples com a possibilidade de sua modificação no futuro.
Trabalhar com estruturas complexas em uma situação normal exigirá soluções não muito padrão, e isso complica bastante o uso de manipuladores padrão. Para criar a grande maioria dos aplicativos, basta uma combinação da biblioteca padrão e um roteador simples.
Processamento de solicitação
Além disso, precisamos de um componente que "escute" as conexões recebidas e redirecione todas as solicitações para o manipulador correto. Esta tarefa pode ser facilmente executada pelo http.Server.
A seguir, mostra que o servidor é responsável por todas as tarefas relacionadas ao processamento da conexão. Isso, por exemplo, funciona no protocolo TLS. Para implementar a chamada http.ListenAndServer, um servidor HTTP padrão é usado.
Agora vamos ver exemplos mais complexos.
Adicionando o Let's Encrypt
Por padrão, nosso aplicativo é executado sobre o protocolo HTTP, mas é recomendável usar o protocolo HTTPS. No Go, isso pode ser feito sem problemas. Se você recebeu um certificado e uma chave privada, basta registrar o ListenAndServeTLS com o certificado e os arquivos de chave corretos.
http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)
Você sempre pode fazer melhor.
Let's Encrypt fornece certificados gratuitos com a capacidade de renovar automaticamente. Para usar o serviço, você precisa do pacote
autocert
.
A maneira mais fácil de configurá-lo é usar o método autocert.NewListener em combinação com http.Serve. O método permite receber e renovar certificados TLS, enquanto o servidor HTTP processa solicitações:
http.Serve(autocert.NewListener("example.com"), nil)
Se abrirmos o
example.com no navegador, obteremos a resposta HTTPS "Olá, mundo!".
Se você precisar de uma configuração mais completa, use o gerenciador autocert.Manager. Em seguida, criamos nossa própria instância http.Server (até agora a usamos por padrão) e adicionamos o gerenciador ao servidor TLSConfig:
m := &autocert.Manager{ Cache: autocert.DirCache("golang-autocert"), Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist("example.org", "www.example.org"), } server := &http.Server{ Addr: ":443", TLSConfig: m.TLSConfig(), } server.ListenAndServeTLS("", "")
Essa é uma maneira fácil de implementar o suporte completo a HTTPS com renovação automática de certificado.
Adicionando rotas personalizadas
O roteador padrão incluído na biblioteca padrão é bom, mas é muito simples. A maioria dos aplicativos requer roteamento mais complexo, incluindo rotas aninhadas e curinga, ou o procedimento para definir padrões e parâmetros de caminho.
Nesse caso, você deve usar os pacotes
gorilla / mux e
go-chi / chi . Vamos aprender a trabalhar com este último - um exemplo é mostrado abaixo.
Dado é o arquivo api / v1 / api.go que contém as rotas para nossa API:
/ HelloResponse is the JSON representation for a customized message type HelloResponse struct { Message string `json:"message"` }
Definimos o prefixo api / vq para rotas no arquivo principal.
Podemos então montar isso em nosso roteador principal sob o prefixo api / v1 / em nosso aplicativo principal:
A simplicidade de trabalhar com rotas complexas no Go torna possível simplificar a estruturação com o atendimento de grandes aplicativos complexos.
Trabalhar com middleware
No caso do processamento intermediário, é usado o empacotamento de um manipulador HTTP com outro, o que possibilita a autenticação, compactação, log e algumas outras funções rapidamente.
Como exemplo, considere a interface http.Handler, com sua ajuda, escrevemos um manipulador com autenticação de usuários do serviço.
func RequireAuthentication(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !isAuthenticated(r) { http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) return }
Existem roteadores de terceiros, por exemplo, chi, que permitem estender a funcionalidade do processamento intermediário.
Trabalhar com arquivos estáticos
A biblioteca padrão Go inclui recursos para trabalhar com conteúdo estático, incluindo imagens, além de arquivos JavaScript e CSS. Eles podem ser acessados através da função http.FileServer. Retorna um manipulador que distribui arquivos de um diretório específico.
func NewRouter() http.Handler { router := chi.NewRouter() r.Get("/{name}", HelloName)
Vale lembrar que http.Dir exibe o conteúdo do diretório se ele não possui o arquivo index.html principal. Nesse caso, para impedir que o diretório seja comprometido, vale a pena usar o pacote não
unindexed
.
Desligamento correto
O Go também possui um recurso como desligar o servidor HTTP corretamente. Isso pode ser feito usando o método Shutdown (). O servidor inicia na goroutine e, em seguida, o canal é ouvido para receber um sinal de interrupção. Assim que o sinal é recebido, o servidor é encerrado, mas não imediatamente, mas após alguns segundos.
handler := server.NewRouter() srv := &http.Server{ Handler: handler, } go func() { srv.Serve(autocert.NewListener(domains...)) }()
Em conclusão
Go é uma linguagem poderosa com uma biblioteca padrão quase universal. Seus recursos padrão são muito amplos e você pode fortalecê-los com a ajuda de interfaces - isso permite que você desenvolva servidores HTTP verdadeiramente confiáveis.
A Skillbox recomenda: