bil*_*ani 1 routing closures for-loop go slice
考虑我有一些字符串路径:
paths := []string{"/path0", "/path1", "/path2" /*... "/path-n"*/ }
// where n is the last path
Run Code Online (Sandbox Code Playgroud)
使用包net/http,我想使用for带有range子句的循环注册此路径的处理程序.这就是我这样做的方式:
for _, path := range paths {
http.HandleFunc(path, handler)
}
// in this case every handler is print the path to the console or to the browser
Run Code Online (Sandbox Code Playgroud)
但我最终得到了相同的输出,这是切片的最后一个元素,所以当我去/path1,输出是/path-n.与其他元素的行为相同,始终打印/path-n.
但如果我用这个:
for _, path := range paths {
http.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, path)
})
}
Run Code Online (Sandbox Code Playgroud)
输出是正确的.
发生了什么事,我错过了什么吗?我需要for通过切片路径或地图给出的循环注册,所以我不能做第二个代码.
你能给我替代完成这项任务吗?
所以问题是你实际使用了这段代码:
for _, path := range paths {
http.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, path)
})
}
Run Code Online (Sandbox Code Playgroud)
你使用了一个函数文字,一个闭包作为处理函数来注册.闭包捕获它们引用的上下文,在您的情况下是path循环变量.
但是只有一个path循环变量,它的值在循环的每次迭代中被覆盖,其最终值将是最后一个路径.规范中的相关部分:对于带有range子句的陈述:
迭代变量可以使用短变量declaration(
:=)的形式由"range"子句声明.在这种情况下,它们的类型被设置为相应迭代值的类型,它们的范围是"for"语句的块; 它们在每次迭代中重复使用.如果迭代变量在"for"语句之外声明,则在执行之后,它们的值将是最后一次迭代的值.
一旦for循环结束,并且您开始发出请求,每个注册的处理函数将发回该单个path变量的值.这就是为什么你看到为所有请求的路径返回的最后一个路径.
解决方案很简单:在每次迭代中创建一个新变量,并在处理函数中使用它:
for _, path := range paths {
path2 := path
http.HandleFunc(path2, func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, path2)
})
}
Run Code Online (Sandbox Code Playgroud)
这里发生的是我们在每次迭代中使用一个简短的变量声明来创建一个新的变量,用path循环变量的值初始化.我们注册的处理函数将引用这个新变量,仅对一个注册路径唯一.
另一个同样好的解决方案是使用带参数的匿名函数来传递path字符串.可能更难理解:
for _, path := range paths {
func(p string) {
http.HandleFunc(p, func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, p)
})
}(path)
}
Run Code Online (Sandbox Code Playgroud)
这里发生的是我们调用一个匿名函数,将当前path值传递给它,并且它仅使用此匿名函数的参数来注册处理函数(并且为每个调用分配了一个新的,不同的局部变量).