douwei2966 2017-05-18 09:44
浏览 86
已采纳

Golang:使用循环切片/地图范围注册多个路由

Consider I have slice of string paths:

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

Using package net/http, I want to register handler for this path using for loop with range clause. This is how I do this:

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

But I ended up with same output which is the last element of slice, so when I go to /path1, the output is /path-n. Same behavior with other element, always print /path-n.

But if I use this:

http.HandleFunc(paths[0], handler)
http.HandleFunc(paths[1], handler)
http.HandleFunc(paths[2], handler)
// ...
http.HandleFunc(paths[n], handler)

The output is correct.

What's going on, did I miss something? I need for loop for registration given by slice of paths or map, so I can't do the second code.

Can you give me the alternative to accomplished this task?

  • 写回答

1条回答 默认 最新

  • duanli9591 2017-05-18 10:16
    关注

    So the problem was that you actually used this code:

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

    You used a function literal, a closure as the handler function to register. Closures capture the context they refer to, in your case the path loop variable.

    But there is only a single path loop variable, its value is overwritten in each iterations of the loop, and its final value will be the last path. Relevant section from the spec: For statements with range clause:

    The iteration variables may be declared by the "range" clause using a form of short variable declaration (:=). In this case their types are set to the types of the respective iteration values and their scope is the block of the "for" statement; they are re-used in each iteration. If the iteration variables are declared outside the "for" statement, after execution their values will be those of the last iteration.

    Once the for loop is finished, and you start making requests, each registered handler function will send back the value of this single path variable. That's why you see the last path returned for all requested paths.

    Solution is easy: create a new variable in each iteration, and use that in the handler function:

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

    What happens here is that we use a short variable declaration in each iteration to create a new variable, initialized with the value of the path loop variable. And the handler function we register will refer to this new variable, unique only to one registered path.

    Another, equally good solution is to use an anonymous function with a parameter to pass the path string. Might be harder to understand though:

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

    What happens here is that we call an anonymous function, passing the current path value to it, and it registers the handler function, using only the parameter of this anonymous function (and there's a new, distinct local variable allocated for each call).

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 IAR程序莫名变量多重定义
  • ¥15 (标签-UDP|关键词-client)
  • ¥15 关于库卡officelite无法与虚拟机通讯的问题
  • ¥15 qgcomp混合物线性模型分析的代码出现错误:Model aliasing occurred
  • ¥100 已有python代码,要求做成可执行程序,程序设计内容不多
  • ¥15 目标检测项目无法读取视频
  • ¥15 GEO datasets中基因芯片数据仅仅提供了normalized signal如何进行差异分析
  • ¥100 求采集电商背景音乐的方法
  • ¥15 数学建模竞赛求指导帮助
  • ¥15 STM32控制MAX7219问题求解答