1

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
  • 2
    I think you would benefit from looking up middleware implementations. negroni is a package you could learn a lot from - especially the negroni.go file. It has a really easy to understand middleware chaining mechanism that you can mimic in your own code (or, just use Negroni). Commented Nov 14, 2014 at 9:36

1 Answer 1

5

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.

Sign up to request clarification or add additional context in comments.

12 Comments

nicolasmerouze.com/… is also important, in order to pass values to the next middlewares. (also: stackoverflow.com/a/24971779/6309)
I am using negroni for middleware but this scenario, have nothing to do with middlware only inheritance.
@zero_coding still, it is a good pratice to follow, to avoid code duplication
Why do golang not provide abstract method? WHat is the reason that go avoid it?
@zero_coding because golang isn't about inheritance. It is about composition. Avoiding code duplication in your case is about chaining middleware (composition), not about "reusing" an "abstract method": no inheritance in Go, think composition. For more, see stackoverflow.com/q/1727250/6309 and programmers.stackexchange.com/questions/157240/…, and nathany.com/good.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.