Eu queria criar um pequeno aplicativo da web independente em Go, o oposto de um aplicativo da web normal, onde os recursos seriam servidos separadamente por meio de um CDN ou servidor HTTP, como o Nginx.
No entanto, se o desempenho não for uma preocupação crítica ou se o aplicativo for destinado a baixo tráfego, o uso de um aplicativo independente simplifica a implantação e a distribuição porque é apenas um binário executável.
Vários pacotes estão disponíveis para incorporar recursos em um aplicativo Go:
Não vou me aprofundar nas especificidades de cada biblioteca, mas prefiro a abordagem bindata devido à sua facilidade de uso e suporte ativo.
Primeiro, vamos criar um index.html
dentro do diretório frontend/
do seu projeto:
<html> <body> Hello, World! </body> </html>
Agora que temos a configuração do projeto e um recurso estático para testar, vamos instalar o bindata usando o comando:
go get -u github.com/jteeuwen/go-bindata/...
Estamos prontos para executar o código backend da aplicação web. Crie um arquivo main.go
e copie o seguinte código:
package main import ( "bytes" "io" "net/http" ) //go:generate go-bindata -prefix "frontend/" -pkg main -o bindata.go frontend/... func static_handler(rw http.ResponseWriter, req *http.Request) { var path string = req.URL.Path if path == "" { path = "index.html" } if bs, err := Asset(path); err != nil { rw.WriteHeader(http.StatusNotFound) } else { var reader = bytes.NewBuffer(bs) io.Copy(rw, reader) } } func main() { http.Handle("/", http.StripPrefix("/", http.HandlerFunc(static_handler))) http.ListenAndServe(":3000", nil) }
Linha importante neste código:
//go:generate go-bindata -prefix "frontend/" -pkg main -o bindata.go frontend/...
A linha acima nos permite executar o comando go-bindata
quando go generate
é chamado. A partir da versão 1.4, você pode executar comandos personalizados durante a fase de geração. É apenas uma questão de adicionar //go:generate command argument...
ao seu arquivo go.
A linha de comando go-bindata
possui vários parâmetros, portanto verifique a documentação sobre como utilizá-la. No nosso caso, dizemos:
-prefix "frontend/"
define parte do nome do caminho para estático
-pkg main
define o nome do pacote usado no código gerado
-o bindata.go
define o nome do arquivo gerado
Depois de executar o comando go generate
, você deverá ver um arquivo gerado chamado bindata.go
. A estrutura do seu projeto deve ficar assim:
. │ ├── bindata.go (auto-generated file) ├── frontend │ └── index.html └── main.go
A lógica para servir arquivos estáticos está na função static_handler
, que recebe a solicitação e verifica se o caminho corresponde ao caminho estático. A verificação é feita através da função Asset
, exportada automaticamente pelo usuário bindata.go
. Se o recurso não existir, retornamos 404
, caso contrário, retornamos o conteúdo do recurso.
O restante do código é para criar o aplicativo da web e vincular nosso modelo static_handler
para corresponder a todas as solicitações recebidas para /
. Se você tiver problemas para entender esse código, verifique a documentação oficial do Go para http package
.
Como um rápido lembrete de como Go lida com pacotes, todos os identificadores serão automaticamente exportados para outros pacotes com o mesmo nome se a primeira letra do nome do identificador começar com letra maiúscula.
Com base nesta regra, o arquivo bindata.go
fornece a função Asset
para o pacote main
. Isso carrega e retorna um ativo para o nome fornecido. Ele retorna um erro se o recurso não puder ser encontrado ou carregado.