考虑一下我有一段字符串路径:
paths := []string{"/path0", "/path1", "/path2" /*... "/path-n"*/ } // where n is the last path
使用package net/http,我想使用for带有range子句的循环为该路径注册处理程序。这就是我的方法:
net/http
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) }) }
但是我最终得到了相同的输出,这是slice的最后一个元素,所以当我转到时/path1,输出为/path-n。与其他元素的行为相同,始终为print /path-n。
/path1
/path-n
但是如果我使用这个:
http.HandleFunc(paths[0], handler) http.HandleFunc(paths[1], handler) http.HandleFunc(paths[2], handler) // ... http.HandleFunc(paths[n], handler)
输出正确。
怎么了,我错过了什么吗?我需要for通过路径或地图切片给出的注册循环,所以我无法执行第二个代码。
您能给我替代完成此任务的方法吗?
因此,问题在于您实际上使用了以下代码:
您使用了函数文字(闭包)作为要注册的处理函数。闭包 捕获 它们所引用的上下文(在您的情况下为pathloop变量)。
path
但是只有一个path循环变量,其值在循环的每次迭代中都会被覆盖,并且其最终值将是最后一条路径。规范中的相关部分:对于带有range子句的语句:
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值传递给它,然后仅使用该匿名函数的参数注册处理程序函数(并且为每个调用分配了一个新的,不同的局部变量)。