使用循环切片/地图的范围注册多个路线



考虑我有字符串路径的一部分:

paths := []string{"/path0", "/path1", "/path2" /*... "/path-n"*/ }
// where n is the last path

使用包net/http,我想使用带有 range 子句for循环为这条路径注册处理程序。这就是我的做法:

for _, path := range paths {
http.HandleFunc(path, handler)
}
// in this case every handler is print the path to the console or to the browser

编辑:基本上提问者使用了以下代码:

for _, path := range paths {
http.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, path)
})
}

但是我最终得到了相同的输出,这是切片的最后一个元素,所以当我转到/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循环来注册路径或地图切片,所以我不能做第二个代码。

你能给我完成这项任务的替代方案吗?

所以问题是你实际上使用了这段代码:

for _, path := range paths {
http.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, path)
})
}

您使用了函数文本、闭包作为要注册的处理程序函数。闭包捕获它们引用的上下文,在您的情况下为path循环变量。

但是只有一个path循环变量,它的值在循环的每次迭代中被覆盖,它的最终值将是最后一条路径。规范中的相关部分: 对于带有range子句的语句:

迭代变量可以通过"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)
})
}

这里发生的情况是,我们在每次迭代中使用一个短变量声明来创建一个变量,使用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值传递给它,它只使用这个匿名函数的参数注册处理程序函数(并且为每个调用分配了一个新的、不同的局部变量)。

最新更新