Adds error formatting
This commit is contained in:
42
error_formatter.go
Normal file
42
error_formatter.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package narco
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type narcoKey int
|
||||||
|
|
||||||
|
const errorFormatterKey narcoKey = 1
|
||||||
|
|
||||||
|
type ErrorFomatter func(ctx context.Context, rw http.ResponseWriter, message interface{}, status int)
|
||||||
|
|
||||||
|
func Error(ctx context.Context, rw http.ResponseWriter, message interface{}, status int) {
|
||||||
|
fmter, ok := ctx.Value(errorFormatterKey).(ErrorFomatter)
|
||||||
|
if ok == false {
|
||||||
|
//default to http formatter
|
||||||
|
http.Error(rw, fmt.Sprintf("%s", message), status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmter(ctx, rw, message, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
var narcoDefaultFormatter ErrorFomatter
|
||||||
|
|
||||||
|
func TextErrorFormatter(ctx context.Context, rw http.ResponseWriter, message interface{}, status int) {
|
||||||
|
rw.Header()["Content-Type"] = []string{"text/plain; charset=utf-8"}
|
||||||
|
rw.WriteHeader(status)
|
||||||
|
fmt.Fprintf(rw, "%s", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BasicHTMLErrorFormatter(ctx context.Context, rw http.ResponseWriter, message interface{}, status int) {
|
||||||
|
rw.Header()["Content-Type"] = []string{"text/html; charset=utf-8"}
|
||||||
|
rw.WriteHeader(status)
|
||||||
|
fmt.Fprintf(rw, `<!doctype html><html><body><h1>Error %d</h1>%s</body></html>`, status, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithErrorFormatter(ctx context.Context, fmter ErrorFomatter) context.Context {
|
||||||
|
return context.WithValue(ctx, errorFormatterKey, fmter)
|
||||||
|
}
|
||||||
29
recovery.go
29
recovery.go
@@ -2,7 +2,6 @@ package narco
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -12,19 +11,14 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StackResponseFormatter func(w io.Writer, err interface{}, stack []byte)
|
type StackResponseFormatter func(err interface{}, stack []byte) string
|
||||||
|
|
||||||
func GoFormatStack(w io.Writer, err interface{}, stack []byte) {
|
func GoFormatStack(err interface{}, stack []byte) string {
|
||||||
fmt.Fprintf(w, "Server internal error: %s\n%s", err, stack)
|
return fmt.Sprintf("Server internal error: %s\n%s", err, stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HtmlFormatStack(w io.Writer, err interface{}, stack []byte) {
|
func HtmlFormatStack(err interface{}, stack []byte) string {
|
||||||
fmt.Fprintf(w, `<html>
|
return fmt.Sprintf(`<h2>Server internal error: %s</h2><pre>%s</pre>`, err, stack)
|
||||||
<body>
|
|
||||||
<h1>Server internal error: %s</h1>
|
|
||||||
<pre>%s</pre>
|
|
||||||
</body>
|
|
||||||
</html>`, err, stack)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Recoverer struct {
|
type Recoverer struct {
|
||||||
@@ -43,27 +37,26 @@ func NewRecoverer() *Recoverer {
|
|||||||
|
|
||||||
func (rec *Recoverer) Wrap() func(chain.Handler) chain.Handler {
|
func (rec *Recoverer) Wrap() func(chain.Handler) chain.Handler {
|
||||||
return func(other chain.Handler) chain.Handler {
|
return func(other chain.Handler) chain.Handler {
|
||||||
return chain.HandlerFunc(func(ctx context.Context, h http.ResponseWriter, req *http.Request) {
|
return chain.HandlerFunc(func(ctx context.Context, rw http.ResponseWriter, req *http.Request) {
|
||||||
defer func() {
|
defer func() {
|
||||||
err := recover()
|
err := recover()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// h.Header()["Content-Type"] = []string{"text/html; charset=utf-8"}
|
|
||||||
h.WriteHeader(http.StatusInternalServerError)
|
|
||||||
|
|
||||||
stack := make([]byte, 8*1024)
|
stack := make([]byte, 8*1024)
|
||||||
stack = stack[:runtime.Stack(stack, rec.StackOtherGoroutine)]
|
stack = stack[:runtime.Stack(stack, rec.StackOtherGoroutine)]
|
||||||
rec.Logger.Printf("PANIC: %s\n%s", err, stack)
|
rec.Logger.Printf("PANIC: %s\n%s", err, stack)
|
||||||
|
|
||||||
|
var message string
|
||||||
if rec.ShowStackInResponse != nil {
|
if rec.ShowStackInResponse != nil {
|
||||||
rec.ShowStackInResponse(h, err, stack)
|
message = rec.ShowStackInResponse(err, stack)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(h, "%s\n", http.StatusText(http.StatusInternalServerError))
|
message = http.StatusText(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
Error(ctx, rw, message, http.StatusInternalServerError)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
other.ServeHTTPContext(ctx, h, req)
|
other.ServeHTTPContext(ctx, rw, req)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,12 @@ func (fs *StaticFileServer) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
|||||||
|
|
||||||
//chain handler interface
|
//chain handler interface
|
||||||
func (fs *StaticFileServer) ServeHTTPContext(ctx context.Context, rw http.ResponseWriter, req *http.Request) {
|
func (fs *StaticFileServer) ServeHTTPContext(ctx context.Context, rw http.ResponseWriter, req *http.Request) {
|
||||||
fs.ServeHTTP(rw, req)
|
err := fs.ServeHTTPError(rw, req)
|
||||||
|
if err != nil {
|
||||||
|
fsErr := err.(StaticFileServerError)
|
||||||
|
//use our own context aware error handler
|
||||||
|
Error(ctx, rw, err.Error(), fsErr.status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// chain wrapper. we just ignore the error, and send the request to
|
// chain wrapper. we just ignore the error, and send the request to
|
||||||
|
|||||||
Reference in New Issue
Block a user