From 7f7ba91570169130cb50b093e9ee148f37e21889 Mon Sep 17 00:00:00 2001 From: Umputun Date: Fri, 9 Feb 2024 15:57:31 -0600 Subject: [PATCH] add a pair of encode/decode json functions --- rest.go | 20 +++++++++++++++++++- rest_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/rest.go b/rest.go index 1b321ec..d8181f3 100644 --- a/rest.go +++ b/rest.go @@ -10,7 +10,7 @@ import ( ) // JSON is a map alias, just for convenience -type JSON map[string]interface{} +type JSON map[string]any // RenderJSON sends data as json func RenderJSON(w http.ResponseWriter, data interface{}) { @@ -97,3 +97,21 @@ func ParseFromTo(r *http.Request) (from, to time.Time, err error) { } return from, to, nil } + +// DecodeJSON decodes json request from http.Request to given type +func DecodeJSON[T any](r *http.Request, res *T) error { + if err := json.NewDecoder(r.Body).Decode(&res); err != nil { + return fmt.Errorf("decode json: %w", err) + } + return nil +} + +// EncodeJSON encodes given type to http.ResponseWriter and sets status code and content type header +func EncodeJSON[T any](w http.ResponseWriter, status int, v T) error { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(status) + if err := json.NewEncoder(w).Encode(v); err != nil { + return fmt.Errorf("encode json: %w", err) + } + return nil +} diff --git a/rest_test.go b/rest_test.go index a31d264..c78b952 100644 --- a/rest_test.go +++ b/rest_test.go @@ -1,6 +1,7 @@ package rest import ( + "bytes" "encoding/json" "errors" "io" @@ -15,7 +16,6 @@ import ( ) func TestRest_RenderJSON(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { j := JSON{"key1": 1, "key2": "222"} RenderJSON(w, j) @@ -35,7 +35,6 @@ func TestRest_RenderJSON(t *testing.T) { } func TestRest_RenderJSONFromBytes(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.NoError(t, RenderJSONFromBytes(w, r, []byte("some data"))) })) @@ -53,7 +52,6 @@ func TestRest_RenderJSONFromBytes(t *testing.T) { } func TestRest_RenderJSONWithHTML(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { j := JSON{"key1": "val1", "key2": 2.0, "html": `
blah
`} require.NoError(t, RenderJSONWithHTML(w, r, j)) @@ -77,7 +75,6 @@ func TestRest_RenderJSONWithHTML(t *testing.T) { } func TestParseFromTo(t *testing.T) { - tbl := []struct { query string from, to time.Time @@ -123,6 +120,49 @@ func TestParseFromTo(t *testing.T) { } +func TestDecodeJSONRequest(t *testing.T) { + type record struct { + Field1 string `json:"field1"` + Field2 int `json:"field2"` + } + + inp := `{"field1":"value1","field2":2}` + req := httptest.NewRequest(http.MethodPost, "/test", bytes.NewBufferString(inp)) + req.Header.Set("Content-Type", "application/json") + + var obj record + err := DecodeJSON(req, &obj) + require.NoError(t, err) + assert.Equal(t, "value1", obj.Field1) + assert.Equal(t, 2, obj.Field2) +} + +func TestEncodeJSONResponse(t *testing.T) { + type record struct { + Field1 string `json:"field1"` + Field2 int `json:"field2"` + } + + obj := record{Field1: "value1", Field2: 2} + w := httptest.NewRecorder() + if err := EncodeJSON(w, http.StatusOK, obj); err != nil { + t.Errorf("Failed to encode JSON response: %v", err) + } + + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, "application/json; charset=utf-8", resp.Header.Get("Content-Type")) + + var decodedObj record + err := json.NewDecoder(resp.Body).Decode(&decodedObj) + require.NoError(t, err) + + assert.Equal(t, obj.Field1, decodedObj.Field1) + assert.Equal(t, obj.Field2, decodedObj.Field2) +} + func getTestHandlerBlah() http.HandlerFunc { fn := func(rw http.ResponseWriter, req *http.Request) { _, _ = rw.Write([]byte("blah"))