0

I am making simple web app using http/server and I use following code for handling routes (credit to this post):

package retable

import (
    "context"
    "fmt"
    "net/http"
    "regexp"
    "strconv"
    "strings"
)

var routes = []route{
    newRoute("GET", "/", home),
}

func newRoute(method, pattern string, handler http.HandlerFunc) route {
    return route{method, regexp.MustCompile("^" + pattern + "$"), handler}
}

type route struct {
    method  string
    regex   *regexp.Regexp
    handler http.HandlerFunc
}

func Serve(w http.ResponseWriter, r *http.Request) {
    var allow []string
    for _, route := range routes {
        matches := route.regex.FindStringSubmatch(r.URL.Path)
        if len(matches) > 0 {
            if r.Method != route.method {
                allow = append(allow, route.method)
                continue
            }
            ctx := context.WithValue(r.Context(), ctxKey{}, matches[1:])
            route.handler(w, r.WithContext(ctx))
            return
        }
    }
    if len(allow) > 0 {
        w.Header().Set("Allow", strings.Join(allow, ", "))
        http.Error(w, "405 method not allowed", http.StatusMethodNotAllowed)
        return
    }
    http.NotFound(w, r)
}

type ctxKey struct{}

func getField(r *http.Request, index int) string {
    fields := r.Context().Value(ctxKey{}).([]string)
    return fields[index]
}

func home(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "home\n")
}

How to serve static files from local "static/" folder on "/" endpoint if other route registration on this endpoint already exists?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Bodito
  • 97
  • 1
  • 14
  • Try this library https://github.com/gorilla/mux#static-files – pedromss Aug 24 '21 at 08:37
  • Just as a note, your custom router is going to have really poor performance, because it just does a naive loop over the registered routes with regex matching. But in general sure you can serve static files from root as long as you have some way to differentiate those requests from other requests, it's all just routing. – Adrian Aug 24 '21 at 13:58

1 Answers1

0

As written, your code expects an exact match for the pattern provided when constructing a route. (See the ^ and $ when constructing the regex.) So you will not be able to handle /static/ requests in the /-pattern's handler.

You may be able to achieve what you want if you make changes to your existing code though. Some options below.

Option 1

Include the static pattern in routes:

var routes = []route{
    newRoute("GET", "/", home),
    newRoute("GET", "/static/(.+)", static),
}

Define an example static HTTP handler function:

func static(w http.ResponseWriter, r *http.Request) {
    log.Println("received static request for ", getField(r, 0))
}

You may want to use a combination of the following to faciliate serving static files:

  • http.StripPrefix
  • http.FileServer
  • http.Dir / http.FS, which embed plays well with

Option 2

Modify newRoute to not use ^ and $ when constructing the regex. However, this may affect expectations elsewhere in your code. Particularly, the / pattern will match all requests, so the ordering of the routes slice becomes important.

    return route{method, regexp.MustCompile(pattern), handler}

Then in home:

func home(w http.ResponseWriter, r *http.Request) {
    if strings.HasPrefix(r.URL.Path, "/static/") {
        log.Println("received static request", r.URL.Path)
        // TODO: actually respond with file
        return
    }
    fmt.Fprint(w, "home\n")
}

Footnote

As a footnote, I would recommend using http.ServeMux / http.DefaultServeMux instead of your Serve implementation. These are battle-tested, will likely be more performant, and may likely have less-surprising behavior than your code.

For instance, http.ServeMux/http.DefaultServeMux clean paths, which the code in the question does not do. So, for example, with the original code as in the question, a request for

curl localhost:8080//

will result in a 404 due to the double slash instead of reaching the home handler.

nishanthshanmugham
  • 2,967
  • 1
  • 25
  • 29