Skip to content

Go: Web Development

The Simplest Web Server

Note

The signature of func(w http.ResponseWriter, r *http.Request) is a common way of handling HTTP requests throughout the Go standard library. It perfectly complies with the expected ServeHTTP method signature of Go's Handler interface:1

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
package main

import (
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte(`
            <html>
                <head>
                <title>Hello World</title>
                </head>
                <body>
                    Hello World!
                </body>
            </html>
        `))
    })

    if err := http.ListenAndServe(":8000", nil); err != nil {
        log.Fatal("ListenAndServe", err)
    }
}

Frequently Asked Questions

How do I create a Handler without having to implement an interface?

Sometimes, you would need to provide an instance that conforms to the Handler interface, but want to avoid actually implementing the interface. In this case, you might use the http.HandlerFunc (please, note the difference between http.HandleFunc and http.HandlerFunc):2

http.HandlerFunc

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

Looking at it carefully, will help you grasp a crucial part of Go's magic - the fact that everything, including functions, is a type, which you can attach further functions upon. In this case, ServeHTTP is actually implemented for us inside http and essentially proxies to the function we pass as parameter to HandlerFunc.

How do I return an HTTP error?

http.Error is the default way of communicating errors to your API's consumers. It takes a message and an error code. The three most often used status codes are:

  • http.StatusBadRequest (400)
  • http.StatusNotFound (404)
  • http.StatusInternalServerError (500)
http.HandleFunc("/xyz", func(writer http.ResponseWriter, request *http.Request) {
        result, err = someFunction()

        if err != nil {
            // This could be a custom message as well
            errMsg := http.StatusText(http.StatusBadRequest)

            http.Error(writer, errMsg, http.StatusBadRequest)

            // NOTE: Do not forget the retunn statement!
            return
        }

        // continue with the logic
})