Controllers

Controllers are a fairly familiar topic in other web development communities.Since most web developers rally around the mighty net/http interface, not manycontroller implementations have caught on strongly. However, there is greatbenefit in using a controller model. It allows for clean, well definedabstractions above and beyond what the net/http handler interface can aloneprovide.

Handler Dependencies

In this example we will experiment with building our own controllerimplementation using some standard features in Go. But first, lets start withthe problems we are trying to solve. Say we are using the render library thatwe talked about in previous chapters:

  1. var Render = render.New(render.Options{})

If we want our http.Handlers to be able access our render.Render instance,we have a couple options.

1. Use a global variable: This isn't too bad for small programs, but whenthe program gets larger it quickly becomes a maintenance nightmare.

2. Pass the variable through a closure to the http.Handler: This is agreat idea, and we should be using it most of the time. The implementation endsup looking like this:

  1. func MyHandler(r *render.Render) http.Handler {
  2. return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  3. // now we can access r
  4. })
  5. }

Case for Controllers

When your program grows in size, you will start to notice that many of yourhttp.Handlers will share the same dependencies and you will have a lot ofthese closurized http.Handlers with the same arguments. The way I like toclean this up is to write a little base controller implementation that affordsme a few wins:

  • Allows me to share the dependencies across http.Handlers that have similar goals or concepts.
  • Avoids global variables and functions for easy testing/mocking.
  • Gives me a more centralized and Go-like mechanism for handling errors.
    The great part about controllers is that it gives us all these things withoutimporting an external package! Most of this functionality comes from clever useof the Go feature set, namely Go structs and embedding. Let's take a look at theimplementation.
  1. package main
  2. import "net/http"
  3. // Action defines a standard function signature for us to use when creating
  4. // controller actions. A controller action is basically just a method attached to
  5. // a controller.
  6. type Action func(rw http.ResponseWriter, r *http.Request) error
  7. // This is our Base Controller
  8. type AppController struct{}
  9. // The action function helps with error handling in a controller
  10. func (c *AppController) Action(a Action) http.Handler {
  11. return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  12. if err := a(rw, r); err != nil {
  13. http.Error(rw, err.Error(), 500)
  14. }
  15. })
  16. }

Thats it! That is all the implementation that we need to have the power ofcontrollers at our fingertips. All we have left to do is implement an examplecontroller:

  1. package main
  2. import (
  3. "net/http"
  4. "gopkg.in/unrolled/render.v1"
  5. )
  6. type MyController struct {
  7. AppController
  8. *render.Render
  9. }
  10. func (c *MyController) Index(rw http.ResponseWriter, r *http.Request) error {
  11. c.JSON(rw, 200, map[string]string{"Hello": "JSON"})
  12. return nil
  13. }
  14. func main() {
  15. c := &MyController{Render: render.New(render.Options{})}
  16. http.ListenAndServe(":8080", c.Action(c.Index))
  17. }

Exercises

  • Extend MyController to have multiple actions for different routes in your application.
  • Play with more controller implementations, get creative.
  • Override the Action method on MyController to render a error HTML page.

原文: https://codegangsta.gitbooks.io/building-web-apps-with-go/content/controllers/index.html