为了账号安全,请及时绑定邮箱和手机立即绑定

嵌套的 ServeMux 总是返回“301 Moved Permanently”

嵌套的 ServeMux 总是返回“301 Moved Permanently”

Go
MMTTMM 2022-05-18 14:17:00
当使用嵌套http.ServeMux来定义我的服务器的端点时,我遇到了这个问题:处理程序总是会以“301 永久移动”响应任何请求,即使 URL 路径应该匹配。例子:package mainimport "net/http"func main() {    api := http.NewServeMux()    api.HandleFunc("ping", func(w http.ResponseWriter, r *http.Request) {        w.Write([]byte("pong\n"))    })    root := http.NewServeMux()    root.Handle("/api/", http.StripPrefix("/api/", api))    http.ListenAndServe(":8080", root)}尝试访问/api/ping时,服务器重定向到/ping(当然返回 404)。同样的事情发生在任何路由下/api/-/api/foo重定向到/foo.我正在使用 Go 1.13 和 curl 7.58。
查看完整描述

3 回答

?
拉丁的传说

TA贡献1789条经验 获得超8个赞

为了让 Firefox 中的测试更容易,我禁用了缓存。否则我将不得不手动清除缓存以获得准确的结果。不过,在使用 Chrome 时,我似乎没有遇到同样的问题。

//img1.sycdn.imooc.com//62848f7d00015a2a11950168.jpg

请记住,我是 Go 新手,但这个问题让我发疯了......我遇到了与你完全相同的行为(显然)......


Go/http 似乎对模式的格式化方式很挑剔..


我搞砸了大约一个小时,终于能够使用以下代码获得一个工作示例:


// Working Code

package main


import "net/http"


func main() {

    root := http.NewServeMux()

    api := http.NewServeMux()


    api.HandleFunc("/ping", myHandlerFunc)


    root.Handle("/api/", http.StripPrefix("/api", api))


    http.ListenAndServe(":8080", root)

}


func myHandlerFunc(w http.ResponseWriter, r *http.Request) {

    w.Write([]byte("pong\n"))

}

我尝试了尽可能多的不同配置(/就正斜杠而言),上面的代码是我让它工作的唯一方法..


具体指:


// Leading forward slash on /ping

api.HandleFunc("/ping", myHandlerFunc)


// The first /api/ is surrounded in forward slashes,

// the second /api only contains a leading forward slash

root.Handle("/api/", http.StripPrefix("/api", api))

将代码更改为此会导致 404...


// DOES NOT WORK!!

package main


import "net/http"


func main() {

    root := http.NewServeMux()

    api := http.NewServeMux()


    api.HandleFunc("/ping", myHandlerFunc)


    root.Handle("/api", http.StripPrefix("/api", api))


    http.ListenAndServe(":8080", root)

}


func myHandlerFunc(w http.ResponseWriter, r *http.Request) {

    w.Write([]byte("pong\n"))

}

我希望这在某种程度上有所帮助!干杯


查看完整回答
反对 回复 2022-05-18
?
慕桂英4014372

TA贡献1871条经验 获得超13个赞

前几天我正在处理这个问题。我认为ServeMux期望有根树(以 开头/),或者它将路径解释为主机名。


模式名称固定,有根路径,如“/favicon.ico”,或有根子树,如“/images/”.... 模式可以选择以主机名开头,将匹配限制为仅该主机上的 URL。


我推测这ping被解释为主机名,这会导致 servermux 的运行方式变得怪异。


在人们编写的其他解决方案中,他们围绕 的位置进行更改,/以使ping路线最终为/ping。


就个人而言,我不喜欢我必须/api/在一个地方和/api另一个地方写作。在我的特殊情况下,我决定使用类似的东西:


root.Handle(createAPIEndpoints("/api/"))

...

func createAPIEndpoints(base string) (string, *http.ServeMux) {


    mux := http.NewServeMux()

    mux.HandleFunc(base+"ping", func(...){...})

    mux.HandleFunc(base+"another", func(...){...})


    // another buried servemux

    mux.Handle(createMoreEndpoints(base+"more/"))


    return base, mux

}

但是,如果您想用处理程序包装处理程序(例如使用StripPrefix或其他类型的中间件,由于返回 2 个值,这不能很好地工作。


查看完整回答
反对 回复 2022-05-18
?
ITMISS

TA贡献1871条经验 获得超8个赞

我遇到了类似的问题,但仅在您尝试/在嵌套路由器下定义根路径的情况下。


为了处理它,我重写了我自己的 stripPefix() 方法,如下所示:


package main


import "net/http"


func main() {

    root := http.NewServeMux()

    api := http.NewServeMux()


    api.HandleFunc("/", myHandlerFunc)


    root.Handle("/api", stripPrefix("/api", api))


    http.ListenAndServe(":8080", root)

}


func myHandlerFunc(w http.ResponseWriter, r *http.Request) {

    w.Write([]byte("OK\n"))

}


// Modified version of http.StripPrefix()

func stripPrefix(prefix string, h http.Handler) http.Handler {

    if prefix == "" {

        return h

    }


    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {

            r2 := new(http.Request)

            *r2 = *r

            r2.URL = new(url.URL)

            *r2.URL = *r.URL

            if len(p) == 0 {

                r2.URL.Path = "/"

            } else {

                r2.URL.Path = p

            }

            h.ServeHTTP(w, r2)

        } else {

            http.NotFound(w, r)

        }

    })

}


查看完整回答
反对 回复 2022-05-18
  • 3 回答
  • 0 关注
  • 218 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信