首页 > 解决方案 > 如何从根 URL 提供静态文件

问题描述

我正在使用 http/server 制作简单的 Web 应用程序,并使用以下代码来处理路由(归功于这篇文章):

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")
}

如果此端点上的其他路由注册已经存在,如何从“/”端点上的本地“static/”文件夹提供静态文件?

标签: gogo-http

解决方案


如所写,您的代码期望与构建路由时提供的模式完全匹配。(在构造正则表达式时请参阅^and $。)因此您将无法在-pattern 的处理程序中处理/static/请求。/

如果您对现有代码进行更改,您可能能够实现您想要的。下面的一些选项。

选项1

将静态模式包含在routes

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

定义一个示例staticHTTP 处理函数:

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

您可能希望使用以下组合来促进提供静态文件:

  • http.StripPrefix
  • http.FileServer
  • http.Dir/ http.FSembed

选项 2

在构造正则表达式时修改newRoute为不使用^和。$但是,这可能会影响代码中其他地方的期望。特别是,该模式将匹配所有请求,因此切片/的顺序变得很重要。routes

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

然后在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")
}

脚注

作为脚注,我建议使用http.ServeMux/http.DefaultServeMux而不是您的Serve实现。这些是经过实战测试的,可能会更高效,并且可能比您的代码具有更少令人惊讶的行为。

例如,http.ServeMux/ http.DefaultServeMuxclean 路径,问题中的代码没有这样做。因此,例如,使用问题中的原始代码,请求

curl localhost:8080//

由于双斜杠而不是到达处理程序,将导致 404 home


推荐阅读