Golang中http.FileServer与自定义路由无法协同工作的问题
Golang中http.FileServer与自定义路由无法协同工作的问题
package router
import (
"errors"
"net/http"
"regexp"
)
type route struct {
Path string
Method string
Handler Handler
}
type Router struct {
routes []route
}
type Handler func(http.ResponseWriter, *http.Request)
// 将方法、路径和处理函数添加到Router结构体的路由切片中
func (r *Router) AddRoute(method, path string, handler Handler) {
r.routes = append(r.routes, route{Path: path, Method: method, Handler: handler})
}
// 为在其他包中使用而创建的路由器方法:Get、Post、Put、Delete
func (r *Router) Get(path string, handler Handler) {
r.AddRoute("GET", path, handler)
}
func (r *Router) Post(path string, handler Handler) {
r.AddRoute("POST", path, handler)
}
func (r *Router) Put(path string, handler Handler) {
r.AddRoute("PUT", path, handler)
}
func (r *Router) Delete(path string, handler Handler) {
r.AddRoute("DELETE", path, handler)
}
// 从客户端请求中查找处理函数(如果它存在于Router中)
func (r *Router) getHandler(path, method string) (Handler, error) {
for _, route := range r.routes {
regex := regexp.MustCompile(route.Path)
if regex.MatchString(path) && route.Method == method {
return route.Handler, nil
}
}
return nil, errors.New("not foundm")
}
// 用于实现http.Handler接口
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
path := req.URL.Path
method := req.Method
handler, err := r.getHandler(path, method)
if err != nil {
http.NotFound(w, req)
return
}
handler(w, req)
}
// 调用此函数可获得 *Router 类型,并使用Router类型的所有方法
func NewRouter() *Router {
return &Router{}
}
以上是我的自定义路由器代码。
package main
import (
router "asciiWeb/back"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"strings"
"text/template"
)
var tmp *template.Template
func init() {
tmp = template.Must(template.ParseGlob("front/*.html"))
}
func main() {
route := router.NewRouter()
route.Get("/api/home", home)
route.Get("/api/index", index)
route.Post("/api/convert", convert)
route.Post("/api/upload", upload)
http.Handle("/api/", route)
http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./static"))))
fmt.Println("http://localhost:8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
return
}
}
func home(w http.ResponseWriter, req *http.Request) {
if err := tmp.ExecuteTemplate(w, "index.html", nil); err != nil {
return
}
}
func convert(w http.ResponseWriter, req *http.Request) {
err := req.ParseForm()
if err != nil {
return
}
font := "--font=" + req.Form.Get("fs")
text := req.Form.Get("name")
res := ""
for _, word := range strings.Fields(text) {
cmd := exec.Command("./ascii", font, word)
output, err := cmd.Output()
if err != nil {
fmt.Println("********", err)
return
}
res += string(output) + "\n"
}
if err := tmp.ExecuteTemplate(w, "index.html", res); err != nil {
return
}
}
如果我运行这段代码,我的CSS文件无法与HTML连接。 如果我使用Go默认的多路复用器,一切正常。为什么我的路由器不能正常工作?
更多关于Golang中http.FileServer与自定义路由无法协同工作的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang中http.FileServer与自定义路由无法协同工作的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你的自定义路由器与 http.FileServer 无法协同工作的原因在于路由匹配逻辑。你的路由器使用正则表达式匹配路径,而 http.DefaultServeMux 使用前缀匹配。当请求 /static/style.css 时,它首先被你的路由器 /api/ 路径捕获,而不是传递给文件服务器。
以下是具体问题和解决方案:
问题分析:
- 你的路由器
getHandler方法使用regexp.MustCompile(route.Path)进行正则匹配 - 注册路由时:
http.Handle("/api/", route)会匹配所有以/api/开头的路径 - 但你的路由器实现中,
/api/这个路径模式会匹配任何包含/api/的路径,包括/static/api/...
解决方案:修改路由器匹配逻辑,使用精确前缀匹配:
// 修改 getHandler 方法,使用前缀匹配而不是正则表达式
func (r *Router) getHandler(path, method string) (Handler, error) {
for _, route := range r.routes {
// 使用 strings.HasPrefix 进行前缀匹配
if strings.HasPrefix(path, route.Path) && route.Method == method {
return route.Handler, nil
}
}
return nil, errors.New("not found")
}
// 或者更精确的路径匹配(推荐)
func (r *Router) getHandler(path, method string) (Handler, error) {
for _, route := range r.routes {
// 精确匹配或路径前缀匹配
if (path == route.Path || strings.HasPrefix(path, route.Path + "/")) && route.Method == method {
return route.Handler, nil
}
}
return nil, errors.New("not found")
}
更好的解决方案:修改主函数中的路由注册顺序和方式:
func main() {
route := router.NewRouter()
route.Get("/api/home", home)
route.Get("/api/index", index)
route.Post("/api/convert", convert)
route.Post("/api/upload", upload)
// 创建自定义的多路复用器
mux := http.NewServeMux()
// 先注册静态文件服务(更具体的路径)
mux.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./static"))))
// 再注册API路由
mux.Handle("/api/", route)
// 可选:添加根路径重定向或处理
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
http.Redirect(w, r, "/api/home", http.StatusFound)
return
}
http.NotFound(w, r)
})
fmt.Println("http://localhost:8080")
if err := http.ListenAndServe(":8080", mux); err != nil {
log.Fatal(err)
}
}
或者修复路由器使其正确处理路径匹配:
// 修改路由结构体,添加是否是前缀匹配的标志
type route struct {
Path string
Method string
Handler Handler
IsPrefix bool // 新增:标识是否是前缀匹配
}
// 修改 AddRoute 方法,自动检测是否是前缀路由
func (r *Router) AddRoute(method, path string, handler Handler) {
isPrefix := strings.HasSuffix(path, "/")
r.routes = append(r.routes, route{
Path: path,
Method: method,
Handler: handler,
IsPrefix: isPrefix,
})
}
// 修改 getHandler 方法
func (r *Router) getHandler(path, method string) (Handler, error) {
for _, route := range r.routes {
matched := false
if route.IsPrefix {
// 前缀匹配:路径以 route.Path 开头
matched = strings.HasPrefix(path, route.Path)
} else {
// 精确匹配
matched = path == route.Path
}
if matched && route.Method == method {
return route.Handler, nil
}
}
return nil, errors.New("not found")
}
根本原因: Go 的 http.Handle 和 http.HandleFunc 使用前缀匹配,而你的路由器使用正则表达式匹配。当注册 /api/ 时,它会拦截所有以 /api/ 开头的请求,但你的正则匹配逻辑可能不够精确,导致其他路径也被错误匹配。
最简单的修复方法是使用 http.NewServeMux() 创建新的多路复用器,并确保路由注册顺序正确(更具体的路由先注册)。

