在循环中声明和分配处理程序给ServeMux是无效的。

huangapple go评论81阅读模式
英文:

Declaring and assigning handlers to ServeMux in the loop does not work

问题

我有以下一段代码,但它的运行结果与预期不符。具体来说,所有对任何端点的请求都被处理为对/banana/auth/banana/description端点的请求。

type Route struct {
    AuthRoute        string
    DescriptionRoute string
}

var routes = [2]Route{
    {
        AuthRoute:        "/apple/auth",
        DescriptionRoute: "/apple/description",
    },
    {
        AuthRoute:        "/banana/auth",
        DescriptionRoute: "/banana/description",
    },
}

// ...
sm := http.NewServeMux()

for i, authServerConfig := range authServerConfigs {
    authHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.Auth(w, r)
    }
    sm.Handle(routes[i].AuthRoute, authHandler)

    descriptionHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.ServeDescription(w, r)
    }
    sm.Handle(routes[i].DescriptionRoute, descriptionHandler)
}

server := &http.Server{
    // ...
    Handler: sm,
    // ...
}
server.ListenAndServe()

当我用下面的代码替换for循环后,它按照我期望的方式正常工作:

    authHandlerApple := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.Auth(w, r)
    }
    sm.Handle(routes[0].AuthRoute, authHandlerApple)

    descriptionHandlerApple := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.ServeDescription(w, r)
    }
    sm.Handle(routes[0].DescriptionRoute, descriptionHandlerApple)

    authHandlerBanana := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.Auth(w, r)
    }
    sm.Handle(routes[1].AuthRoute, authHandlerBanana)

    descriptionHandlerBanana := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.ServeDescription(w, r)
    }
    sm.Handle(routes[1].DescriptionRoute, descriptionHandlerBanana)

问题是,我最初做错了什么,如何避免编写第二个示例中的笨拙代码?

英文:

I have the following piece of code, and it doesn't work as expected. Specifically, all the requests to any endpoints are being handled as requests to either /banana/auth or /banana/description endpoints.

type Route struct {
    AuthRoute        string
    DescriptionRoute string
}

var routes = [2]Route{
    {
        AuthRoute:        "/apple/auth",
        DescriptionRoute: "/apple/description",
    },
    {
        AuthRoute:        "/banana/auth",
        DescriptionRoute: "/banana/description",
    },
}

// ...
sm := http.NewServeMux()

for i, authServerConfig := range authServerConfigs {
    authHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.Auth(w, r)
    }
    sm.Handle(routes[i].AuthRoute, authHandler)

    descriptionHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.ServeDescription(w, r)
    }
    sm.Handle(routes[i].DescriptionRoute, descriptionHandler)
}

server := &http.Server{
    // ...
    Handler: sm,
    // ...
}
server.ListenAndServe()

When I went ahead and replaced the for-loop with these statements, it worked exactly as I wanted:

    authHandlerApple := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.Auth(w, r)
    }
    sm.Handle(routes[0].AuthRoute, authHandlerApple)

    descriptionHandlerApple := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.ServeDescription(w, r)
    }
    sm.Handle(routes[0].DescriptionRoute, descriptionHandlerApple)

    authHandlerBanana := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.Auth(w, r)
    }
    sm.Handle(routes[1].AuthRoute, authHandlerBanana)

    descriptionHandlerBanana := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.ServeDescription(w, r)
    }
    sm.Handle(routes[1].DescriptionRoute, descriptionHandlerBanana)

The question is, what was I originally doing wrong and how can I avoid writing a clunky code as in the second example?

答案1

得分: 2

根据FAQ - 闭包在作为goroutine运行时会发生什么的说明,每个闭包在for循环中共享同一个变量authServerConfig。要修复这个问题,只需在循环中添加authServerConfig := authServerConfig

for i, authServerConfig := range authServerConfigs {
     authServerConfig := authServerConfig

    authHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.Auth(w, r)
    })
    sm.Handle(routes[i].AuthRoute, authHandler)

    descriptionHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.ServeDescription(w, r)
    })
    sm.Handle(routes[i].DescriptionRoute, descriptionHandler)
}
英文:

Per FAQ - What happens with closures running as goroutines, each closure shares that single variable authServerConfig in the for loop. To fix it, just add authServerConfig := authServerConfig in the loop

for i, authServerConfig := range authServerConfigs {
     authServerConfig := authServerConfig

    authHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.Auth(w, r)
    })
    sm.Handle(routes[i].AuthRoute, authHandler)

    descriptionHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authServerConfig.ServeDescription(w, r)
    })
    sm.Handle(routes[i].DescriptionRoute, descriptionHandler)
}

huangapple
  • 本文由 发表于 2022年10月8日 10:50:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/73994039.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定