HTTP handlers exercise (tour of go) The challenge, from Tour of Go, "Exercise: HTTP Handlers":


  Implement the following types and define ServeHTTP methods on them. Register them to handle specific paths in your web server.


type String string

type Struct struct {
    Greeting string
    Punct    string
    Who      string
}


For example, you should be able to register handlers using:

http.Handle("/string", String("I'm a frayed knot."))
http.Handle("/struct", &amp;Struct{"Hello", ":", "Gophers!"})


Solution:

package main

import (
    "log"
    "net/http"
    "fmt"
)

type String string

type Struct struct {
    Greeting    string
    Punct       string
    Who         string
}

func (s String) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if _, err := fmt.Fprint(w, s); err != nil {
        log.Fatal("Error in ServerHTTP of String.")
    }
}

func (s *Struct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if _, err := fmt.Fprint(w, s.Greeting, s.Punct, s.Who); err != nil {
        log.Fatal("Error in ServerHTTP of String.")
    }
}

func main() {
    http.Handle("/string", String("I'm a frayed knot."))
    http.Handle("/struct", &amp;Struct{"Hello", ":", "Gophers!"})
    log.Fatal(http.ListenAndServe("localhost:4000", nil))
}


Any suggestions on how to improve this code?
The logic of trying to write or else log.Fatal is duplicated in the two ServeHTTP implementations. And the log message looks copy pasted, because it's the same for both the String and *Struct versions, for example:


func (s *Struct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if _, err := fmt.Fprint(w, s.Greeting, s.Punct, s.Who); err != nil {
        log.Fatal("Error in ServerHTTP of String.")
    }
}



You could extract the common logic to a function, for example:

func writeOrElseLogFatal(writer func() (int, error), msg string) {
    if _, err := writer(); err != nil {
        log.Fatalf("Error in ServeHTTP of %s", msg)
    }
}


And then call it like this, for example for *Struct:

func (s *Struct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    writer := func() (int, error) {
        return fmt.Fprint(w, s.Greeting, s.Punct, s.Who)
    }
    writeOrElseLogFatal(writer, "Struct")
}


Or you could make things simpler by not logging at all in these functions:

func (s String) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, s)
}

func (s *Struct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, s.Greeting, s.Punct, s.Who)
}