Golang:使用环路切片/贴图的范围注册多个路线

时间:2017-05-18 09:44:18

标签: for-loop go routing closures slice

考虑我有一些字符串路径:

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循环注册,所以我不能做第二个代码。

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

1 个答案:

答案 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值传递给它,并且它仅使用此匿名函数的参数来注册处理函数(并且有一个新的,不同的局部变量为每次通话分配。)