Skip to content

Commit

Permalink
Adding feature to implement custom rest errors
Browse files Browse the repository at this point in the history
The previous version only had the error string as customizable, so I included the ability to modify the whole reponse. See issue ant0ine#193
  • Loading branch information
while-loop committed Mar 8, 2017
1 parent 4602b00 commit 09af94e
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 3 deletions.
25 changes: 23 additions & 2 deletions rest/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
// Note, the responseWriter object instantiated by the framework also implements many other interfaces
// accessible by type assertion: http.ResponseWriter, http.Flusher, http.CloseNotifier, http.Hijacker.
type ResponseWriter interface {

// Identical to the http.ResponseWriter interface
Header() http.Header

Expand All @@ -34,12 +33,34 @@ type ResponseWriter interface {
// eg: rest.ErrorFieldName = "errorMessage"
var ErrorFieldName = "Error"

// This allows to customize the error messages used in the error response payload.
// It defaults to the ErrorFieldName method for compatibility reasons (only recieves error string and http code),
// but can be changed before starting the server.
//
// Sends a json payload of the struct given. Be sure to define the json keys in the struct.
// eg: rest.CustomErrorStruct = &MyCustomStruct{}
var ErrorFunc func(*Request, string, int) interface{}

// Error produces an error response in JSON with the following structure, '{"Error":"My error message"}'
// The standard plain text net/http Error helper can still be called like this:
// http.Error(w, "error message", code)
func Error(w ResponseWriter, error string, code int) {
// Call new method to support backwards compat
ErrorWithRequest(nil, w, error, code)
}

// Error produces an error response in JSON with context of the request
func ErrorWithRequest(r *Request, w ResponseWriter, error string, code int) {
w.WriteHeader(code)
err := w.WriteJson(map[string]string{ErrorFieldName: error})

var errPayload interface{}
if ErrorFunc != nil {
errPayload = ErrorFunc(r, error, code)
} else {
errPayload = map[string]string{ErrorFieldName: error}
}

err := w.WriteJson(errPayload)
if err != nil {
panic(err)
}
Expand Down
61 changes: 60 additions & 1 deletion rest/response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,29 @@ import (
"github.com/ant0ine/go-json-rest/rest/test"
)

func CustomError(r *Request, error string, code int) interface{} {
// r = nil when using test requests
var header string
switch code {
case 400:
header = "Bad Input"
break
case 404:
header = "Not Found"
break
default:
header = "API Error"
}

return map[string]interface{}{
"error": map[string]interface{}{
"header": header,
"code": code,
"message": error,
},
}
}

func TestResponseNotIndent(t *testing.T) {

writer := responseWriter{
Expand All @@ -24,7 +47,7 @@ func TestResponseNotIndent(t *testing.T) {
}
}

// The following tests could instantiate only the reponseWriter,
// The following tests could instantiate only the responseWriter,
// but using the Api object allows to use the rest/test utilities,
// and make the tests easier to write.

Expand Down Expand Up @@ -54,6 +77,24 @@ func TestErrorResponse(t *testing.T) {
recorded.BodyIs("{\"Error\":\"test\"}")
}

func TestCustomErrorResponse(t *testing.T) {

api := NewApi()
ErrorFunc = CustomError

api.SetApp(AppSimple(func(w ResponseWriter, r *Request) {
Error(w, "test", 500)
}))

recorded := test.RunRequest(t, api.MakeHandler(), test.MakeSimpleRequest("GET", "http://localhost/", nil))
recorded.CodeIs(500)
recorded.ContentTypeIsJson()
recorded.BodyIs(`{"error":{"code":500,"header":"API Error","message":"test"}}`)

// reset the package variable to not effect other tests
ErrorFunc = nil
}

func TestNotFoundResponse(t *testing.T) {

api := NewApi()
Expand All @@ -66,3 +107,21 @@ func TestNotFoundResponse(t *testing.T) {
recorded.ContentTypeIsJson()
recorded.BodyIs("{\"Error\":\"Resource not found\"}")
}

func TestCustomNotFoundResponse(t *testing.T) {

api := NewApi()
ErrorFunc = CustomError

api.SetApp(AppSimple(func(w ResponseWriter, r *Request) {
NotFound(w, r)
}))

recorded := test.RunRequest(t, api.MakeHandler(), test.MakeSimpleRequest("GET", "http://localhost/", nil))
recorded.CodeIs(404)
recorded.ContentTypeIsJson()
recorded.BodyIs(`{"error":{"code":404,"header":"Not Found","message":"Resource not found"}}`)

// reset the package variable to not effect other tests
ErrorFunc = nil
}

0 comments on commit 09af94e

Please sign in to comment.