Reputation: 418
I'm building a small, basic web server in Go. If I compile locally and run it, it works great - no issues. The pages show, it's accessible from the localhost, styling intact - all good.
If I then do it inside a Docker container, it doesn't work. It returns "404 page not found". It's like it doesn't have any of the static assets... but this surely can't be - the static assets are intentionally embedded in to the binary using "//go:embed"... and as I said, if you build and run locally it works fine.
I've tried everything I can think of... some steps listed below:
This is just some of the stuff I've tried, with no luck.
I've excluded the css below, it's not really relevant as the index page doesn't even show never mind any styling.
CODE: server code
DOCKERFILE:
FROM golang:1.16.0-alpine3.13 AS build
WORKDIR /app
COPY . .
RUN go build -o server .
FROM golang:1.16.0-alpine3.13
WORKDIR /app
COPY --from=build /app/server .
EXPOSE 8080
CMD ["./server"]
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GOOO</title>
<link rel="stylesheet" href="/assets/static/css/style.css">
</head>
<body>
<h1>GO!!!</h1>
</body>
</html>
DIRECTORY STRUCTURE:
├── Dockerfile
├── go.mod
├── go.sum
├── server.go
├── server_test.go
├── static
│ ├── css
│ │ └── style.css
│ └── index.html
Upvotes: 0
Views: 2334
Reputation: 18370
As per the comments the main issue you are having is that you are serving index.html
from the file system (not the embedded filesystem) and the file does not exist there.
A second issue is that the embedded filesystem will contain a single directory static
so you need to use something like s, err := fs.Sub(static, "static")
so that s.Open("index.html")
will work (otherwise you would need static.Open("static/index.html")
- this applies to your http.FileServer
as well as when serving index.html).
Note: You may not need the below because you could just run http.FileServer
for the /
path (so it serves index.html
and the files in the subdirectories). http.FileServer
will automatically serve index.html
if no filename is provided in the url.
To serve index.html
from the embedded filesystem you could rewrite your function as (untested!):
// default/root handler which serves the index page and associated styling
func indexHandler(w http.ResponseWriter, r *http.Request) {
f, err := s.Open("index.html") // Using `s` from above and assumes its global; better to use handlerfunc and pass filesystem in
if err != nil {
// Send whatever error you want (as file is embedded open should never fail)
return
}
defer f.Close()
w.Header().Set("Content-Type", "text/html")
if _, err := io.Copy(w, f); err != nil { // Write out the file
// Handle error
}
}
The above relies upon a global variable (which I'm not keen on) so I'd transform this into something like:
func IndexHandlerFunc(fs fs.FS, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
f, err := fs.Open("index.html")
if err != nil {
// Send whatever error you want (as file is embedded open should never fail)
return
}
defer f.Close()
w.Header().Set("Content-Type", "text/html")
if _, err := io.Copy(w, f); err != nil { // Write out the file
// Handle error
}
})
}
Upvotes: 1