github.com/creack/ehttp

Golang http wrapper handling errors.


License
MIT
Install
go get github.com/creack/ehttp

Documentation

ehttp

GitHub release GoDoc Build Status Coverage Status Sourcegraph

This package allows you to write http handlers returning an error.

HTTP Status Code

ehttp.NewError and ehttp.NewErrorf can be called to create an error with a custom http status.

If the error is not nil and not an ehttp.Error, then 500 InternalServerError is sent.

If the status is 0, it implies 500.

The same idea applies to panic as well as returned errors.

Error after sending headers

Due to http limitation, we can send the headers only once. If some data has been sent prior to the error, then nothing gets send to the client, the error gets logged on the server side.

Panic

The default ehttp.MWError handles errors, but do not handle panics. In order to send the panic error to the user (or log it after headers are sent), you can use ehttp.MWErrorPanic which wraps ehttp.MWError and use the recovered value as an error.

If the panic value is an ehttp.Error, the proper http status code will be sent to the client when possible.

Support

The package have been tested with:

Examples

net/http

package main

import (
	"log"
	"net/http"

	"github.com/creack/ehttp"
)

func hdlr(w http.ResponseWriter, req *http.Request) error {
	return ehttp.NewErrorf(http.StatusTeapot, "fail")
}

func main() {
	http.HandleFunc("/", ehttp.MWError(hdlr))
	http.Handle("/", ehttp.HandlerFunc(hdlr))
	ehttp.HandleFunc("/", hdlr)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

customized net/http.

package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"

	"github.com/creack/ehttp"
)

func hdlr(w http.ResponseWriter, req *http.Request) error {
	return ehttp.NewErrorf(http.StatusTeapot, "fail")
}

func main() {
	// Define our error format and how to expose it to the client.
	type customError struct {
		Error    string `json:"error"`
		HTTPCode int    `json:"http_code"`
	}
	errorHandler := func(w ehttp.ResponseWriter, req *http.Request,  err error) {
		_ = json.NewEncoder(w).Encode(customError{
			Error:    err.Error(),
			HTTPCode: w.Code(),
		})
	}

	// Define a cutom logger for unexpected events (double header send).
	logger := log.New(os.Stderr, "", log.LstdFlags)

	// Create the mux.
	mux := ehttp.NewServeMux(errorHandler, "application/text; charset=utf-8", false, logger)

	// Register the handler.
	mux.HandleFunc("/", hdlr)

	// Start serve the mux.
	log.Fatal(http.ListenAndServe(":8080", mux))
}

gorilla/mux

package main

import (
	"log"
	"net/http"

	"github.com/creack/ehttp"
	"github.com/gorilla/mux"
)

func hdlr(w http.ResponseWriter, req *http.Request) error {
	return ehttp.NewErrorf(http.StatusTeapot, "fail")
}

func main() {
	router := mux.NewRouter()
	router.Handle("/", ehttp.HandlerFunc(hdlr))
	log.Fatal(http.ListenAndServe(":8080", router))
}

httprouter

package main

import (
	"log"
	"net/http"

	"github.com/creack/ehttp"
	"github.com/creack/ehttp/ehttprouter"
	"github.com/julienschmidt/httprouter"
)

func hdlr(w http.ResponseWriter, req *http.Request, p httprouter.Params) error {
	return ehttp.NewErrorf(http.StatusTeapot, "fail")
}

func main() {
	router := ehttprouter.New(nil, "", false, nil)
	router.GET("/", hdlr)
	log.Fatal(http.ListenAndServe(":8080", router))
}

raw httprouter

package main

import (
	"log"
	"net/http"

	"github.com/creack/ehttp"
	"github.com/creack/ehttp/ehttprouter"
	"github.com/julienschmidt/httprouter"
)

func hdlr(w http.ResponseWriter, req *http.Request, p httprouter.Params) error {
	return ehttp.NewErrorf(http.StatusTeapot, "fail")
}

func main() {
	router := httprouter.New()
	router.GET("/", ehttprouter.MWError(hdlr))
	log.Fatal(http.ListenAndServe(":8080", router))
}

customized httprouter

package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"

	"github.com/creack/ehttp"
	"github.com/creack/ehttp/ehttprouter"
	"github.com/julienschmidt/httprouter"
)

func hdlr(w http.ResponseWriter, req *http.Request, p httprouter.Params) error {
	return ehttp.NewErrorf(http.StatusTeapot, "fail")
}

func main() {
	// Define our error format and how to expose it to the client.
	type customError struct {
		Error    string `json:"error"`
		HTTPCode int    `json:"http_code"`
	}
	errorHandler := func(w ehttp.ResponseWriter, req *http.Request,  err error) {
		_ = json.NewEncoder(w).Encode(customError{
			Error:    err.Error(),
			HTTPCode: w.Code(),
		})
	}

	// Define a cutom logger for unexpected events (double header send).
	logger := log.New(os.Stderr, "", log.LstdFlags)

	// Create the mux.
	router := ehttprouter.NewServeMux(errorHandler, "application/text; charset=utf-8", true, logger)
	router.GET("/", hdlr)
	log.Fatal(http.ListenAndServe(":8080", router))
}