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