ehttp

package module
v0.4.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2016 License: MIT Imports: 12 Imported by: 2

README

ehttp

GoDoc Build Status Coverage Status

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))
}

Documentation

Overview

Package ehttp wraps the standard http package with an error management middleware. Support net/http, gorilla/mux and any router using http.HandleFunc

Example (CustomErrorHandler)
package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/creack/ehttp"
)

func main() {
	// Define how to send errors to the user.
	errorHandler := func(w ehttp.ResponseWriter, req *http.Request, err error) {
		fmt.Fprintf(w, "<<<<<<%s>>>>>>", err)
	}
	mux := ehttp.NewServeMux(errorHandler, "application/text; charset=utf-8", false, nil)
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) error {
		return ehttp.NewErrorf(http.StatusTeapot, "fail")
	})
	log.Fatal(http.ListenAndServe(":8080", mux))
}
Output:

Example (Gorilla)
package main

import (
	"log"
	"net/http"

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

func main() {
	hdlr := func(w http.ResponseWriter, req *http.Request) error {
		return ehttp.NewErrorf(http.StatusTeapot, "fail")
	}
	router := mux.NewRouter()
	router.HandleFunc("/", ehttp.MWError(hdlr))
	log.Fatal(http.ListenAndServe(":8080", router))
}
Output:

Example (GorillaPanic)
package main

import (
	"log"
	"net/http"

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

func main() {
	hdlr := func(w http.ResponseWriter, req *http.Request) error {
		panic(ehttp.NewErrorf(http.StatusTeapot, "big fail"))
	}
	router := mux.NewRouter()
	router.HandleFunc("/", ehttp.MWErrorPanic(hdlr))
	log.Fatal(http.ListenAndServe(":8080", router))
}
Output:

Example (Http)
package main

import (
	"log"
	"net/http"

	"github.com/creack/ehttp"
)

func main() {
	hdlr := func(w http.ResponseWriter, req *http.Request) error {
		return ehttp.NewErrorf(http.StatusTeapot, "fail")
	}
	http.HandleFunc("/", ehttp.MWError(hdlr))
	log.Fatal(http.ListenAndServe(":8080", nil))
}
Output:

Example (HttpPanic)
package main

import (
	"log"
	"net/http"

	"github.com/creack/ehttp"
)

func main() {
	hdlr := func(w http.ResponseWriter, req *http.Request) error {
		panic(ehttp.NewErrorf(http.StatusTeapot, "big fail"))
	}
	http.HandleFunc("/", ehttp.MWErrorPanic(hdlr))
	log.Fatal(http.ListenAndServe(":8080", nil))
}
Output:

Index

Examples

Constants

This section is empty.

Variables

Common errors.

View Source
var (
	ErrNotHijacker   = errors.New("not a net/http.Hijacker")
	ErrNotReaderFrom = errors.New("not an io.ReaderFrom")
)

Common errors.

View Source
var DefaultServeMux = &ServeMux{
	ServeMux: http.DefaultServeMux,
	sendError: func(w ResponseWriter, _ *http.Request, err error) {
		_ = json.NewEncoder(w).Encode(&JSONError{Errors: []string{err.Error()}})
	},
	errorContentType: "application/json; charset=utf-8",
	recoverPanic:     false,
	log:              log.New(os.Stderr, "", log.LstdFlags),
}

DefaultServeMux is the default ServeMux used by Serve. The behavior of the DefaultServeMux is as follows: - Logger: Standard log.Logger. - Content-Type: "application/json; charset=utf-8" - SendError callback: Send errors wraps in a json object with the key "errors" as a type []string. - RecoverPanic: false.

Functions

func HandleError

func HandleError(w ResponseWriter, req *http.Request, err error)

HandleError exposes HandleError from the DefaultServeMux.

func HandleFunc

func HandleFunc(pattern string, handler HandlerFunc)

HandleFunc wraps net/http.HandleFunc with the error middleware.

func HandlePanic

func HandlePanic(err error, e1 interface{}) error

HandlePanic exposes HandlePanic from the DefaultServeMux.

func MWError

func MWError(handler HandlerFunc) http.HandlerFunc

MWError exposes MWError from DefaultServeMux.

func MWErrorPanic

func MWErrorPanic(handler HandlerFunc) http.HandlerFunc

MWErrorPanic exposes MWErrorPanic from DefaultServeMux.

func NewError

func NewError(code int, err error) error

NewError creates a new http error including a status code.

func NewErrorf

func NewErrorf(code int, f string, args ...interface{}) error

NewErrorf creates a new http error including a status code.

Types

type Error

type Error struct {
	// contains filtered or unexported fields
}

Error is a basic error including the http return code.

func (Error) Code

func (e Error) Code() int

Code is an accessor for the error code.

func (Error) Error

func (e Error) Error() string

Error implements the error interface.

func (Error) GetError

func (e Error) GetError() error

GetError exposes the underlying error.

type HandlerFunc

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

HandlerFunc is the custom http handler function extending the standard one with an error return.

func (HandlerFunc) ServeHTTP

func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP implements the http.Handler interface, serving our custom prototype with error support.

type JSONError

type JSONError struct {
	Errors []string `json:"errors"`
}

JSONError is the default struct returned to the client upon error.

type ResponseWriter

type ResponseWriter interface {
	http.ResponseWriter
	Code() int
}

ResponseWriter extends http.ResponseWriter and exposes the http status code.

func NewResponseWriter

func NewResponseWriter(w http.ResponseWriter) ResponseWriter

NewResponseWriter instantiates a new ehttp ResponseWriter.

If the underlying http.ResponseWriter implement net/http.Hijacker, assume it is a *net/http.response and use *github.com/creack/ehttp.response, otherwise, assume it is a *net/http.http2responseWriter and use *github.com/creack/ehttp.http2responseWriter.

type ServeMux added in v0.4.1

type ServeMux struct {
	ServeMux *http.ServeMux
	// contains filtered or unexported fields
}

ServeMux wraps *net/http.ServeMux for ehttp.

Example
package main

import (
	"log"
	"net/http"

	"github.com/creack/ehttp"
)

func main() {
	mux := ehttp.NewServeMux(nil, "application/text; charset=utf-8", false, nil)
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) error {
		return ehttp.InternalServerError
	})
	log.Fatal(http.ListenAndServe(":8080", mux))
}
Output:

Example (CustomLogger)
package main

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

	"github.com/creack/ehttp"
)

func main() {
	// When returning an error after sending the errors, we can't set the http Status.
	// As data as already been sent, we don't want to corrupt it so we log the error server side.
	logger := log.New(os.Stderr, "", log.LstdFlags)
	mux := ehttp.NewServeMux(nil, "application/text; charset=utf-8", true, logger)
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) error {
		panic(ehttp.InternalServerError)
	})
	log.Fatal(http.ListenAndServe(":8080", mux))
}
Output:

Example (Customized)
package main

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

	"github.com/creack/ehttp"
)

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("/", func(w http.ResponseWriter, req *http.Request) error {
		// Return an error.
		return ehttp.NewErrorf(http.StatusTeapot, "fail")
	})

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

Example (Panic)
package main

import (
	"log"
	"net/http"

	"github.com/creack/ehttp"
)

func main() {
	mux := ehttp.NewServeMux(nil, "application/text; charset=utf-8", true, nil)
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) error {
		panic(ehttp.InternalServerError)
	})
	log.Fatal(http.ListenAndServe(":8080", mux))
}
Output:

func NewServeMux added in v0.4.1

func NewServeMux(sendErrorCallback func(ResponseWriter, *http.Request, error), errorContentType string, recoverPanic bool, logger *log.Logger) *ServeMux

NewServeMux emulates net/http.NewServeMux but returns a *github.com/creack/ehttp.ServeMux. Logger default to the default log.Logger if nil. sendErrorCallback default to `fmt.Fprintf(w, "%s\n", err)` if nil.

func (*ServeMux) HandleError added in v0.4.1

func (sm *ServeMux) HandleError(w ResponseWriter, req *http.Request, err error)

HandleError handles the returned error from the MWError middleware. Should not be manually called. Exposed to be accessed from adaptor subpackages. If the error is nil, then no http code is yielded.

func (*ServeMux) HandleFunc added in v0.4.1

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

HandleFunc adds the given handler to the underlying ServeMux.

func (*ServeMux) HandlePanic added in v0.4.1

func (sm *ServeMux) HandlePanic(err error, e1 interface{}) error

HandlePanic handles the panic from the handler. Should not be manually called. Exposed to be accessed from adaptor subpackages.

func (*ServeMux) HandlerFunc added in v0.4.1

func (sm *ServeMux) HandlerFunc(handler func(http.ResponseWriter, *http.Request) error) http.Handler

HandlerFunc converts the github.com/creack/ehttp.HandlerFunc handler to a standard http.Handler. If the recoverPanic flag is set, handle panics and return them as error.

func (*ServeMux) MWError added in v0.4.1

func (sm *ServeMux) MWError(handler HandlerFunc) http.HandlerFunc

MWError is the main middleware. When an error is returned, it send the data to the client if the header hasn't been sent yet, otherwise, log them.

func (*ServeMux) MWErrorPanic added in v0.4.1

func (sm *ServeMux) MWErrorPanic(handler HandlerFunc) http.HandlerFunc

MWErrorPanic wraps MWError and recovers from panic.

func (*ServeMux) ServeHTTP added in v0.4.1

func (sm *ServeMux) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP implements http.Handler interface.

Directories

Path Synopsis
Package ehttprouter wraps httprouter with an error management middleware.
Package ehttprouter wraps httprouter with an error management middleware.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL