Como você fornece um arquivo html estático usando um servidor da Web Go?

91

Como você veicula index.html (ou algum outro arquivo HTML estático) usando um servidor da Web Go?

Eu só quero um arquivo HTML básico e estático (como um artigo, por exemplo) que posso servir de um servidor web go. O HTML deve ser modificável fora do programa go, como seria no caso ao usar modelos HTML.

Este é o meu servidor web que hospeda apenas texto codificado ("Olá, mundo!").

package main

import (
  "fmt"
  "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Hello world!")
}

func main() {
  http.HandleFunc("/", handler)
  http.ListenAndServe(":3000", nil)
}
nairware
fonte

Respostas:

145

Essa tarefa é muito fácil com o pacote Golang net / http.

Tudo que você precisa fazer é:

package main

import (
        "net/http"
)

func main() {
        http.Handle("/", http.FileServer(http.Dir("./static")))
        http.ListenAndServe(":3000", nil)
}

assumindo que os arquivos estáticos estão em uma pasta nomeada staticno diretório raiz do projeto.

Se estiver na pasta static, você terá uma index.htmlchamada de arquivo http://localhost:3000/que resultará na renderização desse arquivo de índice em vez de listar todos os arquivos disponíveis.

Além disso, chamar qualquer outro arquivo nessa pasta (por exemplo http://localhost:3000/clients.html) mostrará esse arquivo, devidamente renderizado pelo navegador (pelo menos Chrome, Firefox e Safari :))

ATUALIZAÇÃO: disponibilizando arquivos de url diferente de "/"

Se você quiser servir arquivos, diga da pasta ./publicem url: localhost:3000/staticVocê deve usar uma função adicional : func StripPrefix(prefix string, h Handler) Handlerassim:

package main

import (
        "net/http"
)

func main() {
        http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./public"))))
        http.ListenAndServe(":3000", nil)
}

Graças a isso, todos os seus arquivos de ./publicestão disponíveis emlocalhost:3000/static

Sem http.StripPrefixfunção, se você tentar acessar o arquivo localhost:3000/static/test.html, o servidor irá procurá-lo em./public/static/test.html

Isso ocorre porque o servidor trata todo o URI como um caminho relativo para o arquivo.

Felizmente, isso é facilmente resolvido com a função integrada.

Jarema
fonte
3
por que lidar com o padrão /static/não é /static?
Bryce
Como servir um arquivo html estático se o arquivo estiver em outro lugar no disco rígido, inteiramente fora do projeto?
iamtoc
Tentei usar o caminho absoluto: / Usuários / nome de usuário / caminho / para / arquivo, e funcionou com êxito da mesma forma.
iamtoc
@Bryce, porque você deseja combinar a subárvore completa ( veja também ). Com o mesmo /staticpadrão (e prefixo de faixa), apenas as http://example.org/staticsolicitações seriam atendidas pelo manipulador FileServer. Isso significa que as solicitações para http://example.org/static/e http://example.org/static/foo.cssetc. falhariam ou seriam tratadas por outro manipulador.
maxschlepzig
É possível servir arquivos estáticos junto com rotas de descanso?
Brain
15

Eu prefiro usar http.ServeFilepara isso ao invéshttp.FileServer . Eu queria a navegação no diretório desabilitada, um erro 404 adequado se arquivos estiverem faltando e uma maneira fácil de criar um caso especial para o arquivo de índice. Dessa forma, você pode simplesmente colocar o binário integrado em uma pasta e ele servirá tudo relativo a esse binário. Claro, você pode usar strings.Replaceon pse tiver os arquivos armazenados em outro diretório.


func main() {
    fmt.Println("Now Listening on 80")
    http.HandleFunc("/", serveFiles)
    log.Fatal(http.ListenAndServe(":80", nil))
}

func serveFiles(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.URL.Path)
    p := "." + r.URL.Path
    if p == "./" {
        p = "./static/index.html"
    }
    http.ServeFile(w, r, p)
}
idiota
fonte
5

NÃO é um servidor FTP: isso é algo diferente do que eu pretendia, que seria servir ao index.html página inicial, como um servidor web normal faria. Por exemplo, quando vou para mydomain.com no meu navegador, quero index.htmlrenderizado.

Isso é principalmente o que descreve " Escrevendo aplicativos da Web " e como um projeto hugo (gerador de site HTML estático) faz.

Trata-se de ler um arquivo e responder com um ContentType "text / html":

func (server *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    err := server.renderFile(w, r.URL.Path)
    if err != nil {
        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        w.WriteHeader(http.StatusNotFound)
        server.fn404(w, r)
    }
}

com renderFile()essencialmente lendo e definindo o tipo certo:

 file, err = ioutil.ReadFile(server.MediaPath + filename)
 if ext != "" {
    w.Header().Set("Content-Type", mime.TypeByExtension(ext))
 }
VonC
fonte
Talvez eu não tenha feito a pergunta corretamente. Isso não é um meio de construir um servidor de arquivos, semelhante a um servidor FTP? Isso é algo diferente do que eu pretendia, que seria servir a página inicial index.html, como um servidor web normal faria. Por exemplo, quando vou para mydomain.com no meu navegador, quero index.html renderizado. Não quero ver um diretório de arquivos armazenado no sistema de arquivos do servidor web. Posso editar a pergunta para reformular se minha pergunta for enganosa.
nairware,
@nairware ok, reescrevi a resposta
VonC,
Portanto, não há como criar um site clássico em Go que use páginas HTML e páginas de servidor? Deve ser um site estático ou baseado em modelo?
Spero
0

Isso é fácil no golang como:

package main

import (
    "log"
    "net/http"
)

func main() {
    log.Fatal(http.ListenAndServe(":8080", http.FileServer(http.Dir("."))))
}

`

Você pode apenas fazer isso e certificar-se de manter seu arquivo HTML como index.html

Nishant Raj
fonte
0

Exemplo de como servir arquivo mp3 personalizado:

r := http.NewServeMux()
r.HandleFunc("/file/*", func(w http.ResponseWriter, r *http.Request) {

    // Prepare file path
    pathFile := strings.ReplaceAll(r.RequestURI, "/file/", "./my_path/")
    f, err := os.Open(pathFile)
    if f == nil || err != nil {
        return
    }

    // Read file into memory
    fileBytes, err := ioutil.ReadAll(f)
    if err != nil {
        log.Println(err)
        _, _ = fmt.Fprintf(w, "Error file bytes")
        return
    }

    // Check mime type
    mime := http.DetectContentType(fileBytes)
    if mime != "audio/mpeg" {
        log.Println("Error file type")
        _, _ = fmt.Fprintf(w, "Error file type")
        return
    }

    // Custom headers
    r.Header.Add("Content-Type", "audio/mpeg")
    r.Header.Add("Cache-Control", "must-revalidate, post-check=0, pre-check=0")
    r.Header.Add("Content-Description", "File Transfer")
    r.Header.Add("Content-Disposition", "attachment; filename=file.mp3")
    r.Header.Add("Content-Transfer-Encoding", "binary")
    r.Header.Add("Expires", "0")
    r.Header.Add("Pragma", "public")
    r.Header.Add("Content-Length", strconv.Itoa(len(fileBytes)))
    http.ServeFile(w, r, pathFile)
})
log.Fatal(http.ListenAndServe(":80", r))
Vitams
fonte