英文:
golang passing http.ResponseWriter
问题
我正在尝试弄清楚在编写Web应用程序时是否有可能不必在每个地方都传递http.ResponseWriter。我正在设置一个简单的MVC Web框架,发现自己不得不在各种函数中传递http.ResponseWriter,而它只在最后一个函数中使用。
Routes包
// 包含HTTP请求和变量的结构体
type UrlInfo struct {
Res http.ResponseWriter
Req *http.Request
Vars map[string]string
}
func HandleFunc(handlepath string, runfunc func(*UrlInfo)) {
// 设置处理程序并设置结构体
http.HandleFunc(getHandlePath(handlepath), func(w http.ResponseWriter, r *http.Request) {
url := new(UrlInfo)
url.Res = w
url.Req = r
url.Vars = parsePathVars(r.URL.Path, handlepath)
runfunc(url)
})
}
// 解析文件并发送到ResponseWriter
func View(w http.ResponseWriter, path string, data interface{}) {
// 从views文件夹中获取文件
temp, err := template.ParseFiles(path + ".html")
if err != nil {
// 找不到HTML文件,发送错误
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
temp.ExecuteTemplate(w, temp.Name(), data)
}
}
Controller包
import (
"routes"
)
func init() {
// 构建HandleFunc
routes.HandleFunc("/home/", home)
}
func home(urlinfo *routes.UrlInfo) {
info := make(map[string]string)
info["Title"] = urlinfo.Vars["title"]
info["Body"] = "Body Info"
gi.View(urlinfo.Res, "pages/about", info)
}
我希望在home函数中不必传递任何东西,只需将其再次传递给View函数进行输出。希望能够在一个地方设置它并在需要时从中获取。这对于以相同方式与routes包通信的多个包也很有用。
欢迎任何想法、技巧或窍门。谢谢。
英文:
I am trying to figure out if its possible to not have to pass along the http.ResponseWriter everywhere i go when programming a web application. I am setting up a simple mvc web framework and i find myself having to pass the http.ResponseWriter through various functions when its only used in lets say the last function.
Routes package
<!-- language: lang-go -->
// Struct containing http requests and variables
type UrlInfo struct {
Res http.ResponseWriter
Req *http.Request
Vars map[string]string
}
func HandleFunc(handlepath string, runfunc func(*UrlInfo)) {
// Set handler and setup struct
http.HandleFunc(getHandlePath(handlepath), func(w http.ResponseWriter, r *http.Request) {
url := new(UrlInfo)
url.Res = w
url.Req = r
url.Vars = parsePathVars(r.URL.Path, handlepath)
runfunc(url)
})
}
// Parse file and send to responsewriter
func View(w http.ResponseWriter, path string, data interface{}) {
// Go grab file from views folder
temp, err := template.ParseFiles(path+".html")
if err != nil {
// Couldnt find html file send error
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
temp.ExecuteTemplate(w, temp.Name(), data)
}
}
Controller package
<!-- language: lang-go -->
import (
"routes"
)
func init() {
// Build handlefunc
routes.HandleFunc("/home/", home)
}
func home(urlinfo *routes.UrlInfo) {
info := make(map[string]string)
info["Title"] = urlinfo.Vars["title"]
info["Body"] = "Body Info"
gi.View(urlinfo.Res, "pages/about", info)
}
I would like to not have to pass anything in the home function just so i can pass it again to the view function to spit out. Would be nice to be able to set it in one place and pull from it whenever needed. This would also nice in regard to multiple packages that communicate to the routes package in the same regard.
Any and all thoughts, tips or tricks are welcome. Thanks.
答案1
得分: 5
有几种方法可以做到这一点。关键是要弄清楚你实际上需要从传递的 ResponseWriter 中得到什么。听起来你只需要运用一点函数组合的技巧。
改变你的设计,使得 View 函数返回一个 io.Reader 和一个错误,然后将其传递给 ResponseWriter。这是一个完全未经测试的示例:
func View(path string, data interface{}) (io.Reader, error) {
// 从 views 文件夹中获取文件
temp, err := template.ParseFiles(path+".html")
if err != nil {
// 找不到 html 文件,返回错误
return nil, err
} else {
buf := bytes.Buffer()
temp.ExecuteTemplate(buf, temp.Name(), data)
return buf
}
}
func HandleFunc(handlepath string, runfunc func(*UrlInfo) (io.Reader, error)) {
// 设置处理程序并设置结构体
http.HandleFunc(getHandlePath(handlepath),
func(w http.ResponseWriter, r *http.Request) {
url := new(UrlInfo)
url.Res = w
url.Req = r
url.Vars = parsePathVars(r.URL.Path, handlepath)
rdr, err := runfunc(url)
io.Copy(w, rdr);
})
}
使用这种方法,唯一需要关心 http ResponseWriter 的是 HandleFunc 函数。
英文:
There are a number of ways you can do this. The trick is to figure out what is you actually need from the ResponseWriter you are passing through. It sounds like you just need to exercise a little function composition.
Change your design so that the View returns an io.Reader, and an error that you can then pipe into the ResponseWriter. Here's a completely untested example:
func View(path string, data interface{}) (io.Reader, error) {
// Go grab file from views folder
temp, err := template.ParseFiles(path+".html")
if err != nil {
// Couldnt find html file send error
return nil, err
} else {
buf := bytes.Buffer()
temp.ExecuteTemplate(buf, temp.Name(), data)
return buf
}
}
func HandleFunc(handlepath string, runfunc func(*UrlInfo) (io.Reader, error)) {
// Set handler and setup struct
http.HandleFunc(getHandlePath(handlepath),
func(w http.ResponseWriter, r *http.Request) {
url := new(UrlInfo)
url.Res = w
url.Req = r
url.Vars = parsePathVars(r.URL.Path, handlepath)
rdr, err := runfunc(url)
io.Copy(w, rdr);
})
}
With this the only thing that has to worry about the http ResponseWriter is your HandleFunc function.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论