DavSanchez
DavSanchez

Reputation: 901

Second FileServer serves html but not images

I'm running into the following problem with Go and gorilla/mux routers when building a simple API. I'm sure it's the usual stupid mistake that is right on my face, but I can't see it.

Simplified project structure

|--main.go
|
|--public/--index.html
|        |--image.png
|
|--img/--img1.jpg
|     |--img2.jpg
|     |--...
|...

main.go

package main

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

var Router = mux.NewRouter()

func InitRouter() {
    customers := Router.PathPrefix("/customers").Subrouter()

    customers.HandleFunc("/all", getAllCustomers).Methods("GET")
    customers.HandleFunc("/{customerId}", getCustomer).Methods("GET")
    // ...
    // Registering whatever middleware
    customers.Use(middlewareFunc)

    users := Router.PathPrefix("/users").Subrouter()

    users.HandleFunc("/register", registerUser).Methods("POST")
    users.HandleFunc("/login", loginUser).Methods("POST")
    // ...

    // Static files (customer pictures)
    var dir string
    flag.StringVar(&dir, "images", "./img/", "Directory to serve the images")
    Router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))

    var publicDir string
    flag.StringVar(&publicDir, "public", "./public/", "Directory to serve the homepage")
    Router.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(publicDir))))
}

func main() {
    InitRouter()
    // Other omitted configuration
    server := &http.Server{
        Handler: Router,
        Addr:    ":" + port,
        // Adding timeouts
        WriteTimeout: 15 * time.Second,
        ReadTimeout:  15 * time.Second,
    }

    err := server.ListenAndServe()
    // ...
}

Subroutes work OK, with middleware and all. The images under img are correctly served if I go to localhost:5000/static/img1.png.

The thing is, going to localhost:5000 serves the index.html that resides in public, but then localhost:5000/image.png is a 404 not found instead.

What is happening here?

Upvotes: 2

Views: 286

Answers (1)

colm.anseo
colm.anseo

Reputation: 22027

Change this line:

// handles '/' and *ONLY* '/'
Router.Handle("/",
        http.StripPrefix("/", http.FileServer(http.Dir(publicDir))))

To this:

// handles '/' and all sub-routes
Router.PathPrefix("/").Handler(
        http.StripPrefix("/",http.FileServer(http.Dir(publicPath))))

Basically, in your original code, the router for / is handling this path and only that path (no sub-routes).

You may wonder why your original code "worked" for at least one file (index.html). The reason is the http.FileServer given a path that is a directory - not a file - will default to serving the index page file index.html (see FileServer source).

Using PathPrefix allows the (fileserver) handler to accept all URL paths below the path /.

Upvotes: 1

Related Questions