Skip to content
This repository has been archived by the owner on Aug 20, 2021. It is now read-only.

Commit

Permalink
go-macaron#49 custom 500 handler
Browse files Browse the repository at this point in the history
  • Loading branch information
unknwon committed Jul 28, 2015
1 parent 5ccc184 commit 8fb3b05
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 18 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Macaron [![Build Status](https://drone.io/github.com/Unknwon/macaron/status.png)

Package macaron is a high productive and modular design web framework in Go.

##### Current version: 0.6.5
##### Current version: 0.6.6

## Getting Started

Expand Down
14 changes: 11 additions & 3 deletions macaron.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
"github.com/Unknwon/macaron/inject"
)

const _VERSION = "0.6.5.0720"
const _VERSION = "0.6.6.0728"

func Version() string {
return _VERSION
Expand Down Expand Up @@ -83,11 +83,19 @@ func NewWithLogger(out io.Writer) *Macaron {
m.Router.m = m
m.Map(m.logger)
m.Map(defaultReturnHandler())
m.notFound = func(resp http.ResponseWriter, req *http.Request) {
c := m.createContext(resp, req)
m.notFound = func(ShouldStartWith http.ResponseWriter, req *http.Request) {
c := m.createContext(ShouldStartWith, req)
c.handlers = append(c.handlers, http.NotFound)
c.run()
}
m.internalServerError = func(rw http.ResponseWriter, req *http.Request, err error) {
c := m.createContext(rw, req)
c.handlers = append(c.handlers, func(rw http.ResponseWriter, req *http.Request, err error) {
http.Error(rw, err.Error(), 500)
})
c.Map(err)
c.run()
}
return m
}

Expand Down
17 changes: 13 additions & 4 deletions return_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,37 @@ func canDeref(val reflect.Value) bool {
return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr
}

func isError(val reflect.Value) bool {
_, ok := val.Interface().(error)
return ok
}

func isByteSlice(val reflect.Value) bool {
return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8
}

func defaultReturnHandler() ReturnHandler {
return func(ctx *Context, vals []reflect.Value) {
rv := ctx.GetVal(inject.InterfaceOf((*http.ResponseWriter)(nil)))
res := rv.Interface().(http.ResponseWriter)
resp := rv.Interface().(http.ResponseWriter)
var respVal reflect.Value
if len(vals) > 1 && vals[0].Kind() == reflect.Int {
res.WriteHeader(int(vals[0].Int()))
resp.WriteHeader(int(vals[0].Int()))
respVal = vals[1]
} else if len(vals) > 0 {
respVal = vals[0]
if isError(respVal) {
ctx.internalServerError(ctx.Resp, ctx.Req.Request, respVal.Interface().(error))
return
}
}
if canDeref(respVal) {
respVal = respVal.Elem()
}
if isByteSlice(respVal) {
res.Write(respVal.Bytes())
resp.Write(respVal.Bytes())
} else {
res.Write([]byte(respVal.String()))
resp.Write([]byte(respVal.String()))
}
}
}
22 changes: 19 additions & 3 deletions return_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package macaron

import (
"errors"
"net/http"
"net/http/httptest"
"testing"
Expand All @@ -24,7 +25,7 @@ import (

func Test_Return_Handler(t *testing.T) {
Convey("Return with status and body", t, func() {
m := Classic()
m := New()
m.Get("/", func() (int, string) {
return 418, "i'm a teapot"
})
Expand All @@ -38,8 +39,23 @@ func Test_Return_Handler(t *testing.T) {
So(resp.Body.String(), ShouldEqual, "i'm a teapot")
})

Convey("Return with error", t, func() {
m := New()
m.Get("/", func() error {
return errors.New("what the hell!!!")
})

resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)

So(resp.Code, ShouldEqual, http.StatusInternalServerError)
So(resp.Body.String(), ShouldEqual, "what the hell!!!\n")
})

Convey("Return with pointer", t, func() {
m := Classic()
m := New()
m.Get("/", func() *string {
str := "hello world"
return &str
Expand All @@ -54,7 +70,7 @@ func Test_Return_Handler(t *testing.T) {
})

Convey("Return with byte slice", t, func() {
m := Classic()
m := New()
m.Get("/", func() []byte {
return []byte("hello world")
})
Expand Down
17 changes: 15 additions & 2 deletions router.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ type Router struct {
*routeMap
namedRoutes map[string]*Leaf

groups []group
notFound http.HandlerFunc
groups []group
notFound http.HandlerFunc
internalServerError func(http.ResponseWriter, *http.Request, error)
}

func NewRouter() *Router {
Expand Down Expand Up @@ -261,6 +262,18 @@ func (r *Router) NotFound(handlers ...Handler) {
}
}

// Configurable handler which is called when route handler returns
// error. If it is not set, default handler is used.
// Be sure to set 500 response code in your handler.
func (r *Router) InternalServerError(handlers ...Handler) {
r.internalServerError = func(rw http.ResponseWriter, req *http.Request, err error) {
c := r.m.createContext(rw, req)
c.handlers = append(r.m.handlers, handlers...)
c.Map(err)
c.run()
}
}

func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if t, ok := r.routers[req.Method]; ok {
h, p, ok := t.Match(req.URL.Path)
Expand Down
30 changes: 25 additions & 5 deletions router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package macaron

import (
"errors"
"net/http"
"net/http/httptest"
"testing"
Expand All @@ -24,7 +25,7 @@ import (

func Test_Router_Handle(t *testing.T) {
Convey("Register all HTTP methods routes", t, func() {
m := Classic()
m := New()
m.Get("/get", func() string {
return "GET"
})
Expand Down Expand Up @@ -135,7 +136,7 @@ func Test_Router_Handle(t *testing.T) {
})

Convey("Register all HTTP methods routes with combo", t, func() {
m := Classic()
m := New()
m.SetURLPrefix("/prefix")
m.Use(Renderer())
m.Combo("/", func(ctx *Context) {
Expand Down Expand Up @@ -243,7 +244,7 @@ func Test_Router_URLFor(t *testing.T) {

func Test_Router_Group(t *testing.T) {
Convey("Register route group", t, func() {
m := Classic()
m := New()
m.Group("/api", func() {
m.Group("/v1", func() {
m.Get("/list", func() string {
Expand All @@ -261,7 +262,7 @@ func Test_Router_Group(t *testing.T) {

func Test_Router_NotFound(t *testing.T) {
Convey("Custom not found handler", t, func() {
m := Classic()
m := New()
m.Get("/", func() {})
m.NotFound(func() string {
return "Custom not found"
Expand All @@ -274,9 +275,28 @@ func Test_Router_NotFound(t *testing.T) {
})
}

func Test_Router_InternalServerError(t *testing.T) {
Convey("Custom internal server error handler", t, func() {
m := New()
m.Get("/", func() error {
return errors.New("Custom internal server error")
})
m.InternalServerError(func(rw http.ResponseWriter, err error) {
rw.WriteHeader(500)
rw.Write([]byte(err.Error()))
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, 500)
So(resp.Body.String(), ShouldEqual, "Custom internal server error")
})
}

func Test_Router_splat(t *testing.T) {
Convey("Register router with glob", t, func() {
m := Classic()
m := New()
m.Get("/*", func(ctx *Context) string {
return ctx.Params("*")
})
Expand Down

0 comments on commit 8fb3b05

Please sign in to comment.