Demystifying HTTP Handlers in Golang

Rashmin Mudunkotuwa
Geek Culture
Published in
4 min readJul 29, 2021

Lately I’ve been getting into learning GoLang and building HTTP services using the core packages which were available in Golang itself. Namely I played around the net/http package and how it allows developers to easily set-up a quick HTTP service.

The net/http package uses Handlers to handle HTTP requests which are sent to a specific path. When digging deep into the package I’ve noticed that there are various methods and types which are defined in the package which can be somewhat confusing at the start for a beginner who is looking forward to use the package to build a HTTP service. The intention of this article is to give a brief understanding about the Handler structure in Golang .

Above all, there is the Handler interface. The Handler interface is an interface with a single method ServeHTTP which takes a http.Response and a http.Request as inputs.

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

This method can be said as the core method which “responds” to a given http request . We can implement this interface in a HandlerClass of our own and give our own implementation of ServeHTTP.

type helloWorldhandler structfunc (h helloWorldHandler) ServeHTTP(w ResponseWriter, r *Request){     fmt.Fprintf(w,"HeloWorld")}

To map a given Handler to a certain path, we can use the http.Handle() method.

func Handle(pattern string, handler Handler)

When this is called Go will register the given Handler to the given path and will invoke the ServeHTTP method of the Handler when a request hits the path.

http.Handle("/helloWorld",helloWorldHandler)

In this example, the ServeHTTP method of helloWorldHandler will be invoked when a http Request hits “/helloWorld” path.

One disadvantage of the above method of handling HTTP methods can be said as If we want to handle a lot of HTTP Requests from different paths, we need to create Handler types for each single one implementing the Handler interface. For this reason the net/http package has provided a special function HandleFunc.

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

The duty of HandleFunc is the same as http.Handle(w,r), which is registering a given path to a handler, but it does that in a different way. If you look at the signature of the above method you can see that it takes a function as the second argument of the HandleFunc() instead of a Handler type like in the http.Handle().

So in this case we can give http.HandleFunc() any function which has the signature func(ResponseWriter, *Request) and it would use that function to handle to request which are hitting the given path. So the need of creating Handler types for each and every path is eliminated here.

myHelloWorldFunc := func (w ResponseWriter, r *Request) 
{fmt.Fprintf(w,"HeloWorld Function")}
http.HandleFunc("/helloWorld", myHelloWorldFunc)

At last there is the type HandlerFunc. This can be said a adapter which allows us to use ordinary functions as http.Handler s.

If you look at the definition of the type,

type HandlerFunc func(ResponseWriter, *Request)

you can see that HandlerFunc is a type which has the type of func(ResponseWriter, *Request). the HandlerFunc type itself implements the ServeHTTP function like follows.

type HandlerFunc func(ResponseWriter, *Request)func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {   f(w,r)}

So we can say that the HandlerFunc function itself implements the http.Handler interface. As shown above, if we look inside the implementation of the ServeHTTP method we can see that , all it does is calling the main function which HandlerFunc is based on. If we take a look at an example,

myHelloWorldFunc2 := func (w ResponseWriter, r *Request) 
{fmt.Fprintf(w,"HeloWorld Function")}
handlerFromHelloFunc := http.HandlerFunc(myHelloWorldFunc2)http.Handle("/helloWorld", handlerFromHelloFunc)

So in the above example we can see that, the function myHelloWorldFunc2 is converted to the http.Handler type by http.HandlerFunc() and then it is used as a normal handler in http.Handle method.

This concludes this brief article which was intended to give a little understanding about the various implementations we can use to handle a HTTP request using the core net/http package. To give a summary, we talked about 3 main ways we can handle a given path of a HTTP request.

  1. Creating a Handler type, implementing the ServeHTTP method and using http.Handle(path, Handler) to register it.
  2. Creating a function with the signature func (w ResponseWriter, r *Request) and using http.HandleFunc(path, function) to register it
  3. Creating a function and converting it to a Handler using the http.HandlerFunc support type and using it in the http.Handle(path, handler) method to register it.

If you reached this far of the article, I hope you you have gained even a little more understanding about the different HTTP Handlers in Golang. If you have any questions or corrections, please feel free to comment.

Follow for more content regarding APIs, Micro-services, Java and Golang :)

Rashmin Mudunkotuwa
Rashmin Mudunkotuwa

Written by Rashmin Mudunkotuwa

Software Engineer | Interested in Cloud Computing, Microservices, API Development, and Software as a whole.

Responses (4)

What are your thoughts?