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.

softshipper
  • 32,463
  • 51
  • 192
  • 400
  • 2
    I think you would benefit from looking up middleware implementations. [negroni](https://github.com/codegangsta/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). – Simon Whitehead Nov 14 '14 at 09:36

1 Answers1

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.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • http://nicolasmerouze.com/share-values-between-middlewares-context-golang/ is also important, in order to pass values to the next middlewares. (also: http://stackoverflow.com/a/24971779/6309) – VonC Nov 14 '14 at 12:28
  • I am using negroni for middleware but this scenario, have nothing to do with middlware only inheritance. – softshipper Nov 14 '14 at 14:07
  • @zero_coding still, it is a good pratice to follow, to avoid code duplication – VonC Nov 14 '14 at 14:08
  • Why do golang not provide abstract method? WHat is the reason that go avoid it? – softshipper Nov 14 '14 at 15:25
  • 2
    @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 http://stackoverflow.com/q/1727250/6309 and http://programmers.stackexchange.com/questions/157240/when-rob-pike-says-go-is-about-composition-what-exactly-does-he-mean, and http://nathany.com/good/. – VonC Nov 14 '14 at 15:43
  • This composition idology does not want go into my brain. @VonC in my case, are you sure that I need middlware chaining. In my code, I borrow sign controller from base controller, and want execute method certain line. – softshipper Nov 14 '14 at 21:24
  • @zero_coding I confirm. Reasoning in term of base, abstract or inheritance is fighting how Go operates, mainly with functions able to produce other functions (like a function taking a handler, and producing another handler: chaining). You should call your base controller, and, depending on your chaining, call the activate handler if needed, sharing a common struct . – VonC Nov 14 '14 at 21:34
  • for example, now I have to set self.lang = header.Language(self.Request) everytime on the sub controller(also sign up controller too), but my wish is that the base controller this going to do that for me, not assign it everytime in sign up controller. How can I do that? – softshipper Nov 15 '14 at 20:53
  • I am trying many hours to configure it out, how to do it in the right way. I give up. I post the code on git, and hope that anyone is going to tell me, how do it in the right way https://gist.github.com/kostonstyle/3a8ceb66dea53a164e35 https://gist.github.com/kostonstyle/fc3a3f74373e718a10e9 – softshipper Nov 15 '14 at 21:08
  • @zero_coding I see you still have that "base controller" notion, with your "signup controller" embedding Controller in it (which is a way to simulate inheritance in Go). You won't get rid of code duplication by clinging to inheritance-based design in Go. That is simply not how Go is designed. The articles I mention in my answer do a good job of describing the kind of chaining all the Go web server out there rely on. – VonC Nov 15 '14 at 21:13
  • Thanks you so much, that you are trying to help me. I will try it again and then post my code again on gist. I will let you know then. – softshipper Nov 15 '14 at 21:50
  • @zero_coding https://github.com/squiidz/fur provide an interesting toolkit, which also aims to avoid code duplication. – VonC Nov 15 '14 at 22:28