-
[ Golang ] Rest Api 스펙이 틀릴때 (타입이 틀릴때) Json decode(Unmarshal) 하기개발/golang 2021. 4. 2. 21:08
외부 혹은 내부 Rest API 를 사용할때 문서에 적혀있는 스펙과 다른 타입을 반환할때가 종종있다.
이럴때 그냥 그대로 json decode (unmarshal) 를 하게될경우 err 가 발생하는것을 확인할수 있다.
따라서 아래와 같이 예방할수있다.
package example_wrong_type_api_spec import ( "encoding/json" "strconv" "strings" "time" ) type ProductResponseSpec struct { Name string `json:"name"` Price int `json:"price"` CreatedAt time.Time `json:"created_at"` } type ProductResponseSpecProtect struct { Name string `json:"name"` Price SafeInt `json:"price"` CreatedAt SafeTime `json:"created_at"` } type SafeTime struct { time.Time } type SafeInt struct { Value int } func (m *SafeInt) UnmarshalJSON(data []byte) error { strData := string(data) if strData == "null" || strData == `""` { return nil } n, err := strconv.Atoi(strings.ReplaceAll(strData, "\"", "")) *m = SafeInt{n} return err } func (m *SafeTime) UnmarshalJSON(data []byte) error { if string(data) == "null" || string(data) == `""` { return nil } tt, err := time.Parse(`"`+time.RFC3339+`"`, string(data)) *m = SafeTime{tt} return err } type HttpClient interface { Request() string } type HttpResponse struct { StatusCode int Url string Body string } type ProductProxyAPI struct { h HttpClient } func NewProductProxyAPI(h HttpClient) ProductProxyAPI { return ProductProxyAPI{h: h} } func (p ProductProxyAPI) Search() (ProductResponseSpec, error) { response := p.h.Request() spec := ProductResponseSpec{} if err := json.Unmarshal([]byte(response), &spec); err != nil { return ProductResponseSpec{}, err } return spec, nil } func (p ProductProxyAPI) SafeSearch() (ProductResponseSpecProtect, error) { response := p.h.Request() spec := ProductResponseSpecProtect{} if err := json.Unmarshal([]byte(response), &spec); err != nil { return ProductResponseSpecProtect{}, err } return spec, nil } package example_wrong_type_api_spec_test import ( "github.com/golang/mock/gomock" "github.com/hoyeonUM/golang-example/example_wrong_type_api_spec" mock_example_wrong_type_api_spec "github.com/hoyeonUM/golang-example/example_wrong_type_api_spec/mock" "github.com/stretchr/testify/assert" "testing" "time" ) var response = ` { "name" : "test product", "price" : "", "created_at" : "" } ` func TestAPI(t *testing.T) { t.Run(`given wrong type response when use non safe struct json unmarshal then return error`, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() m := mock_example_wrong_type_api_spec.NewMockHttpClient(ctrl) m.EXPECT().Request().Return(response).Times(1) api := example_wrong_type_api_spec.NewProductProxyAPI(m) _, err := api.Search() assert.Error(t, err) }) t.Run(`given wrong type response when use safe struct json unmarshal then return struct and nil`, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() m := mock_example_wrong_type_api_spec.NewMockHttpClient(ctrl) m.EXPECT().Request().Return(response).Times(1) api := example_wrong_type_api_spec.NewProductProxyAPI(m) result, err := api.SafeSearch() assert.Nil(t, err) assert.Equal(t, "test product", result.Name) assert.Equal(t, 0, result.Price.Value) assert.Equal(t, time.Time{}.Format(time.RFC3339), result.CreatedAt.Format(time.RFC3339)) }) }
'개발 > golang' 카테고리의 다른 글
[ Golang ] JSON Object 키 넣은 순서대로 정렬하기 (0) 2021.04.02 [ Golang ] Go 루틴 최대갯수를 제한하기 (0) 2021.04.02 mariadb 에서 elasticsearch 실시간 연동하기 (0) 2021.03.30 [ Golang ] Go 루틴을 테스트 하기 (gomock) (0) 2021.03.11