英文:
Avoiding code duplication
问题
我正在尝试编写一个网络应用程序,并努力编写清晰的代码。
我有一个用于处理传入请求的控制器,以及所有控制器都将借用其字段的基本控制器。
这是我的基本控制器:
type Controller struct {
Request *http.Request
Response http.ResponseWriter
// 请求语言
lang string
// HTML 部分
Title string
body string
head string
content string
view string
errors []string
success []string
}
// 这里是后续的方法
func (self *Controller) renderHeadView() { .....
以及我的注册控制器:
type Controller struct {
base.Controller
user *account
}
func (self *Controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
self.Title = "Sign Up"
self.Request = r
self.Response = rw
self.lang = header.Language(self.Request)
self.user = &account{lang: self.lang}
switch self.Request.Method {
case "GET":
self.get()
case "POST":
if err := self.post(); err != nil {
self.get()
} else {
// 如果一切顺利
return
}
}
self.RenderResponseView()
}
以及我的激活控制器:
type Controller struct {
base.Controller
}
func (self *Controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
self.Title = "Activate account"
self.Request = r
self.Response = rw
self.lang = header.Language(self.Request)
self.RenderResponseView()
}
如你所见,ServeHTTP 方法看起来非常相似。我正在考虑将 ServeHTTP 移到基本控制器中,并提供一个方法来调用特殊工作。为了阐明我的意思,请看下面的代码片段(基本控制器):
func (self *Controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
self.Title = "Activate account"
self.Request = r
self.Response = rw
self.lang = header.Language(self.Request)
// 这里将会是特殊工作的函数
function()
self.RenderResponseView()
}
我不知道如何实现这一点。如果有人能给我一些建议,我会非常高兴。
英文:
I am trying to write a web application and given the effort to write clean code.
I have a controller for handle incoming request and base controller that all controller will borrow the fields.
This is my base controller
type Controller struct {
Request *http.Request
Response http.ResponseWriter
// Reqeust language
lang string
// HTML parts
Title string
body string
head string
content string
view string
errors []string
success []string
}
// And methods followed here
func (self *Controller) renderHeadView() { .....
and my sign up controller
type Controller struct {
base.Controller
user *account
}
func (self *Controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
self.Title = "Sign Up"
self.Request = r
self.Response = rw
self.lang = header.Language(self.Request)
self.user = &account{lang: self.lang}
switch self.Request.Method {
case "GET":
self.get()
case "POST":
if err := self.post(); err != nil {
self.get()
} else {
// If everything was successfully
return
}
}
self.RenderResponseView()
}
and my activate controller
type Controller struct {
base.Controller
}
func (self *Controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
self.Title = "Activate account"
self.Request = r
self.Response = rw
self.lang = header.Language(self.Request)
self.RenderResponseView()
}
As you can see, the ServeHTTP method looks pretty much the same. I am thinking of to move ServeHTTP into base controller and then provide an method to call for special work. To clarify what I mean look at the following code snippet(base controller)
func (self *Controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
self.Title = "Activate account"
self.Request = r
self.Response = rw
self.lang = header.Language(self.Request)
// here would come function for special work
function()
self.RenderResponseView()
}
I have no idea how to provide this implementation. I would be very happy, if someone could give me some suggestions.
答案1
得分: 5
一个很好的参考文章是《Go中的中间件:最佳实践和示例》。
在使用Go编写Web应用程序时,我们遇到的第一个代码异味是代码重复。
在处理请求之前,我们经常需要记录请求、将应用程序错误转换为HTTP 500错误、对用户进行身份验证等等。而且我们需要为每个处理程序执行大部分这些操作。
我们可以创建一个带有闭包的函数。但是如果我们有多个类似的函数,它将变得像JavaScript中的回调地狱一样糟糕。我们不希望出现这种情况。
所以我们可以编写一个处理程序并将另一个处理程序传递给它。
loggingHandler(recoverHandler(indexHandler))
因此,中间件将是类似于
func (http.Handler) http.Handler
的东西。
这样我们传递一个处理程序并返回一个处理程序。最后,我们只有一个处理程序,并且可以使用http.Handle(pattern, handler)
来调用它。
Alice
Alice是一个更加优雅地链接处理程序的小包。此外,我们可以创建一个常见的处理程序列表,并在每个路由中重用它们,像这样:
func main() {
commonHandlers := alice.New(loggingHandler, recoverHandler)
http.Handle("/about", commonHandlers.ThenFunc(aboutHandler))
http.Handle("/", alice.New(commonHandlers, bodyParserHandler).ThenFunc(indexHandler))
http.ListenAndServe(":8080", nil)
}
问题解决了。我们现在有一个符合惯用法并使用标准接口的中间件系统。Alice只有50行代码,所以它是一个非常小的依赖项。
英文:
A good article to refer to is "Middlewares in Go: Best practices and examples"
> The first code smell we encounter when writing a web application in Go is code duplication.
Before processing the request, we will often need to log the request, convert app errors into HTTP 500 errors, authenticate users, etc. And we need to do most of these things for each handler.
> We could create a function with a closure. But if we have multiple functions like that, it will become as bad as callback spaghetti in Javascript. We don't want that.
> So we can write a handler and pass another handler to it.
loggingHandler(recoverHandler(indexHandler))
> So a middleware would be something like func (http.Handler) http.Handler
This way we pass a handler and returns a handler. At the end we have one handler and can be called with http.Handle(pattern, handler)
Alice
> Alice is a small package to chain handlers more elegantly. Furthermore, we can create a common list of handlers and reuse them for each route like this:
func main() {
commonHandlers := alice.New(loggingHandler, recoverHandler)
http.Handle("/about", commonHandlers.ThenFunc(aboutHandler))
http.Handle("/", alice.New(commonHandlers, bodyParserHandler).ThenFunc(indexHandler))
http.ListenAndServe(":8080", nil)
}
> Problem solved. We now have a middleware system that is idiomatic and use standard interfaces. Alice is 50 lines of code, so it is a very small dependency.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论