Skip to content
🎉 httpz v1.1.0 released
DocsHandwriting httpzCentrailized error handling

The general process of error handling in net/http is as follows:

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /hello", helloHandler)
	http.ListenAndServe(":8080", mux)
}
 
func helloHandler(w http.ResponseWriter, r *http.Request) {
	condition := false
	if !condition {
		http.Error(w, "bad request", http.StatusBadRequest)
		return
	}
	
	fmt.Fprintf(w, "hello world")
}

Imagine you want to add centralized error handling when using net/http.

In that case, helloHandler must return an error:

func helloHandlerWithErr(w http.ResponseWriter, r *http.Request) error {
	condition := false
	if !condition {
		return fmt.Errorf("condition is false")
	}
 
	fmt.Fprintf(w, "hello world")
	return nil
}

However, http.HandleFunc only accepts handler functions with the signature func(http.ResponseWriter, *http.Request):

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

You thought of writing an adapter to convert

type HandlerFunc func(http.ResponseWriter, *http.Request) error

into

type HttpHandlerFunc func(http.ResponseWriter, *http.Request)

You implement centralized error handling in the adapter:

func Adapter(fn HandlerFunc) HttpHandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		err := fn(w, r)
 
		// Centralized error handling
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
		}
	}
}

You have implemented centralized error handling, but you need to wrap each HandlerFunc with Adapter:

mux.HandleFunc("GET /hello", Adapter(helloHandlerWithErr))

This is cumbersome, and we need to further optimize it.

Create a struct ServeMux that embeds http.ServeMux:

type ServeMux struct {
	http.ServeMux
}

Override the HandleFunc method of ServeMux to accept HandlerFunc instead of the original HttpHandlerFunc:

func (m *ServeMux) HandleFunc(pattern string, handler HandlerFunc) {
	m.ServeMux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
		err := handler(w, r)
 
		if err != nil {
			http.Error(w, "some msg", http.StatusBadRequest)
		}
	})
}

This way, you don’t need to use Adapter for wrapping. The complete implementation is available at https://github.com/aeilang/httpz/blob/main/http.go