Go 1.22 HTTP Mux:同一路径同时处理接口与文件系统
解决Go标准库ServeMux中根路径与静态文件共存及自定义404的问题
一、实现根路径走index函数,其他路径走静态文件服务
你的当前代码中,重复注册GET /路由会导致先注册的Handler生效,后注册的被忽略。要实现GET /由index处理、GET /{anythingelse}由静态文件服务处理,可通过一个中间Handler来区分请求路径:
package main import ( "embed" "log" "net/http" "io/fs" ) //go:embed assets var staticFiles embed.FS func main() { // 从嵌入文件系统中获取assets子目录 htmlContent, err := fs.Sub(staticFiles, "assets") if err != nil { log.Fatalf("Failed to get assets subdir: %v", err) } staticServer := http.FileServer(http.FS(htmlContent)) mux := http.NewServeMux() // 统一处理根路径及静态文件请求 mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { // 根路径请求交给index函数处理 index(w, r) } else { // 其他路径交给静态文件服务处理 staticServer.ServeHTTP(w, r) } }) log.Println("Server starting on :8080") log.Fatal(http.ListenAndServe(":8080", mux)) } // index 处理根路径的模板渲染逻辑 func index(w http.ResponseWriter, r *http.Request) { // 此处替换为你的模板解析、执行代码 w.WriteHeader(http.StatusOK) w.Write([]byte("Welcome to the Index Page!")) }
二、标准库ServeMux实现类似httprouter的NotFound机制
标准库http.ServeMux没有内置的NotFound回调,但可以通过以下两种方式实现自定义404处理:
方法1:通过包装ResponseWriter捕获状态码
创建一个自定义的ResponseWriter来记录响应状态,若ServeMux返回404,则调用自定义NotFound逻辑:
// statusRecorder 包装ResponseWriter以捕获响应状态码 type statusRecorder struct { http.ResponseWriter status int } func (sr *statusRecorder) WriteHeader(code int) { sr.status = code sr.ResponseWriter.WriteHeader(code) } // customNotFound 自定义404处理函数 func customNotFound(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) w.Write([]byte("Oops! The page you're looking for doesn't exist.")) } func main() { // ... 初始化静态文件、mux及路由的代码 ... // 用自定义Handler包裹ServeMux customHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sr := &statusRecorder{w, http.StatusOK} mux.ServeHTTP(sr, r) // 若ServeMux返回404,调用自定义NotFound处理 if sr.status == http.StatusNotFound { customNotFound(w, r) } }) log.Println("Server starting on :8080") log.Fatal(http.ListenAndServe(":8080", customHandler)) }
方法2:注册兜底路由(Go 1.22+推荐)
Go 1.22及以上版本支持/*这种尾部通配符路由,可在所有具体路由之后注册一个兜底路由,处理所有未匹配的请求:
func main() { // ... 初始化静态文件、mux及根路径路由的代码 ... // 注册兜底路由,处理所有未被匹配的GET请求 mux.HandleFunc("GET /*", customNotFound) log.Println("Server starting on :8080") log.Fatal(http.ListenAndServe(":8080", mux)) }
若使用Go 1.22之前的版本,可注册GET /作为兜底路由(需确保它是最后注册的),但这种方式会匹配所有未被更具体路由处理的请求,需注意与静态文件服务的逻辑兼容。
内容的提问来源于stack exchange,提问作者George Livanoss
相关产品推荐
相关产品推荐

