Consider the following helloHandler, which returns an error:
func helloHandler(w http.ResponseWriter, r *http.Request) error {
w.Write([]byte("hello httpz"))
return nil
}
httpz simply writes an adapter to convert the above handler into the standard library handler below:
http.HandleFunc("GET /hello", func(w http.ResponseWriter, r *http.Request) {
err := helloHandler(w, r)
if err != nil {
// Global error handling function
ErrorHandlerFunc(w, err)
}
})
This allows us to handle errors centrally. Consider the following example:
// ...
// mux.Get("/hello", helloHandler)
func helloHandler(w http.ResponseWriter, r *http.Request) error {
name := r.URL.Query().Get("name")
if name == "" {
return httpz.NewHTTPError(http.StatusBadRequest, "name is required")
}
fmt.Fprintf(w, "hello %s", name)
return nil
}
When we visit http://localhost:8080/hello, and name is an empty string, you will see: {"msg":"name is required"}
, which is the response returned by our default error handling function.
Default Error Handling Function
// default centralized error handling function.
// only the *HTTPError will trigger error response.
func DefaultErrHandlerFunc(err error, w http.ResponseWriter) {
if he, ok := err.(*HTTPError); ok {
rw := NewHelperRW(w)
rw.JSON(he.StatusCode, he)
} else {
slog.Error(err.Error())
}
}
Note that the default error handling function only triggers a response for errors of type *HTTPError
. Other types of errors will only log the error. The reason for this design is that when users return an HTTPError
, they clearly know that the error will trigger a response, reducing the possibility of redundant response returns.
You can also customize your own global error handling function:
func main() {
mux := httpz.NewServeMux()
mux.ErrHandlerFunc = func(err error, w http.ResponseWriter) {
// It has already been checked that err != nil
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
In this case, all non-nil errors will trigger a response.