Skip to content

Commit 09af94e

Browse files
committed
Adding feature to implement custom rest errors
The previous version only had the error string as customizable, so I included the ability to modify the whole reponse. See issue ant0ine#193
1 parent 4602b00 commit 09af94e

File tree

2 files changed

+83
-3
lines changed

2 files changed

+83
-3
lines changed

rest/response.go

+23-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
// Note, the responseWriter object instantiated by the framework also implements many other interfaces
1212
// accessible by type assertion: http.ResponseWriter, http.Flusher, http.CloseNotifier, http.Hijacker.
1313
type ResponseWriter interface {
14-
1514
// Identical to the http.ResponseWriter interface
1615
Header() http.Header
1716

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

36+
// This allows to customize the error messages used in the error response payload.
37+
// It defaults to the ErrorFieldName method for compatibility reasons (only recieves error string and http code),
38+
// but can be changed before starting the server.
39+
//
40+
// Sends a json payload of the struct given. Be sure to define the json keys in the struct.
41+
// eg: rest.CustomErrorStruct = &MyCustomStruct{}
42+
var ErrorFunc func(*Request, string, int) interface{}
43+
3744
// Error produces an error response in JSON with the following structure, '{"Error":"My error message"}'
3845
// The standard plain text net/http Error helper can still be called like this:
3946
// http.Error(w, "error message", code)
4047
func Error(w ResponseWriter, error string, code int) {
48+
// Call new method to support backwards compat
49+
ErrorWithRequest(nil, w, error, code)
50+
}
51+
52+
// Error produces an error response in JSON with context of the request
53+
func ErrorWithRequest(r *Request, w ResponseWriter, error string, code int) {
4154
w.WriteHeader(code)
42-
err := w.WriteJson(map[string]string{ErrorFieldName: error})
55+
56+
var errPayload interface{}
57+
if ErrorFunc != nil {
58+
errPayload = ErrorFunc(r, error, code)
59+
} else {
60+
errPayload = map[string]string{ErrorFieldName: error}
61+
}
62+
63+
err := w.WriteJson(errPayload)
4364
if err != nil {
4465
panic(err)
4566
}

rest/response_test.go

+60-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,29 @@ import (
66
"github.com/ant0ine/go-json-rest/rest/test"
77
)
88

9+
func CustomError(r *Request, error string, code int) interface{} {
10+
// r = nil when using test requests
11+
var header string
12+
switch code {
13+
case 400:
14+
header = "Bad Input"
15+
break
16+
case 404:
17+
header = "Not Found"
18+
break
19+
default:
20+
header = "API Error"
21+
}
22+
23+
return map[string]interface{}{
24+
"error": map[string]interface{}{
25+
"header": header,
26+
"code": code,
27+
"message": error,
28+
},
29+
}
30+
}
31+
932
func TestResponseNotIndent(t *testing.T) {
1033

1134
writer := responseWriter{
@@ -24,7 +47,7 @@ func TestResponseNotIndent(t *testing.T) {
2447
}
2548
}
2649

27-
// The following tests could instantiate only the reponseWriter,
50+
// The following tests could instantiate only the responseWriter,
2851
// but using the Api object allows to use the rest/test utilities,
2952
// and make the tests easier to write.
3053

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

80+
func TestCustomErrorResponse(t *testing.T) {
81+
82+
api := NewApi()
83+
ErrorFunc = CustomError
84+
85+
api.SetApp(AppSimple(func(w ResponseWriter, r *Request) {
86+
Error(w, "test", 500)
87+
}))
88+
89+
recorded := test.RunRequest(t, api.MakeHandler(), test.MakeSimpleRequest("GET", "http://localhost/", nil))
90+
recorded.CodeIs(500)
91+
recorded.ContentTypeIsJson()
92+
recorded.BodyIs(`{"error":{"code":500,"header":"API Error","message":"test"}}`)
93+
94+
// reset the package variable to not effect other tests
95+
ErrorFunc = nil
96+
}
97+
5798
func TestNotFoundResponse(t *testing.T) {
5899

59100
api := NewApi()
@@ -66,3 +107,21 @@ func TestNotFoundResponse(t *testing.T) {
66107
recorded.ContentTypeIsJson()
67108
recorded.BodyIs("{\"Error\":\"Resource not found\"}")
68109
}
110+
111+
func TestCustomNotFoundResponse(t *testing.T) {
112+
113+
api := NewApi()
114+
ErrorFunc = CustomError
115+
116+
api.SetApp(AppSimple(func(w ResponseWriter, r *Request) {
117+
NotFound(w, r)
118+
}))
119+
120+
recorded := test.RunRequest(t, api.MakeHandler(), test.MakeSimpleRequest("GET", "http://localhost/", nil))
121+
recorded.CodeIs(404)
122+
recorded.ContentTypeIsJson()
123+
recorded.BodyIs(`{"error":{"code":404,"header":"Not Found","message":"Resource not found"}}`)
124+
125+
// reset the package variable to not effect other tests
126+
ErrorFunc = nil
127+
}

0 commit comments

Comments
 (0)