Where is index.html in an embedded directory?

Viewed 708

I am trying to embed a static site (and SPA) into my Go code. The high level structure of my project is

.
├── web.go
└── spa/
    └── index.html

My intent is to have http://localhost:8090/ serving index.html.

The relevant code to do that is

//go:embed spa
var spa embed.FS

log.Info("starting api server")
r := mux.NewRouter()
r.Handle("/", http.FileServer(http.FS(spa)))
log.Fatal(http.ListenAndServe(":8090", r))

When accessing http://localhost:8090/, I get

  • a directory listing page with a single link spa
  • upon clicking on this link I get a 404 page not found

How should I set this up?

2 Answers

File paths in an embedded directory are prefixed by the path used in the //go:embed directive. The embedded file system path for index.html is ​spa/index.html.

Create a sub file system rooted at the spa directory and serve that file system. The path for index.html in the sub file system is index.html.

sub, _ := fs.Sub(spa, "spa")
r.Handle("/", http.FileServer(http.FS(sub)))

https://pkg.go.dev/io/fs#Sub

Run an example on the playground.

This approach works with any mux including the Gorilla Mux. Here's the code for Gorilla where r is a *mux.Router:

sub, _ := fs.Sub(spa, "spa")
r.PathPrefix("/").Handler(http.FileServer(http.FS(sub)))

Run an example on the playground

With Gorilla Mux you need to specify a path prefix:

package main

import (
    "embed"
    "fmt"
    "github.com/gorilla/mux"
    "log"
    "net/http"
)

//go:embed spa
var spa embed.FS

func main() {
    log.Println("starting api server")
    r := mux.NewRouter()
    r.PathPrefix("/spa/").Handler(http.StripPrefix("/", http.FileServer(http.FS(spa))))
    log.Fatal(http.ListenAndServe(":8090", r))
}

This will route requests to <host>/spa/* to the handler. Then you must strip the prefix / as the contents of spa directory have the spa/ prefix without the leading /:

    b, _ := spa.ReadFile("spa/index.html")
    fmt.Println(string(b)) // file contents

To wrap it up:

http://localhost:8090/spa/index.html is routed to the r.PathPrefix("/spa/") handler. The route though is /spa/index.html, strip the first / resulting in spa/index.html and this finally matches the file path in the embedded variable.

Related