diff --git a/pkg/util/httputils/httputils.go b/pkg/util/httputils/httputils.go index fb0fb3c988..1c25800484 100644 --- a/pkg/util/httputils/httputils.go +++ b/pkg/util/httputils/httputils.go @@ -54,8 +54,13 @@ type JSONClientError struct { Data Error } +type JSONClientErrorMsg struct { + Error *JSONClientError +} + func (e *JSONClientError) Error() string { - return fmt.Sprintf("JSONClientError: %s %d %s", e.Details, e.Code, e.Class) + errMsg := JSONClientErrorMsg{Error: e} + return jsonutils.Marshal(errMsg).String() } func headerExists(header *http.Header, key string) bool { @@ -224,34 +229,45 @@ func ParseJSONResponse(resp *http.Response, err error, debug bool) (http.Header, ce.Class = "redirect" return nil, nil, &ce } else { - ce := JSONClientError{} + ce := JSONClientError{ + Code: resp.StatusCode, + Details: resp.Status, + } if jrbody == nil { - ce.Code = resp.StatusCode - ce.Details = resp.Status return nil, nil, &ce } - jrbody2, err := jrbody.Get("error") - if err == nil { - ecode, err := jrbody2.Int("code") - if err == nil { - ce.Code = int(ecode) - ce.Details, _ = jrbody2.GetString("message") - ce.Class, _ = jrbody2.GetString("title") - return nil, nil, &ce - } else { - ce.Code = resp.StatusCode - ce.Details = jrbody2.String() - return nil, nil, &ce + jrbody1, err := jrbody.GetMap() + if err != nil { + err = jrbody.Unmarshal(&ce) + if err != nil { + ce.Details = err.Error() + } + return nil, nil, &ce + } + var jrbody2 jsonutils.JSONObject + if len(jrbody1) > 1 { + jrbody2 = jsonutils.Marshal(jrbody1) + } else { + for _, v := range jrbody1 { + jrbody2 = v } } - - err = jrbody.Unmarshal(&ce) - if err != nil { - return nil, nil, err - } else { - return nil, nil, &ce + if ecode, _ := jrbody2.GetString("code"); len(ecode) > 0 { + code, err := strconv.Atoi(ecode) + if err != nil { + ce.Class = ecode + } else { + ce.Code = code + } } + if edetail := jsonutils.GetAnyString(jrbody2, []string{"message", "detail", "error_msg"}); len(edetail) > 0 { + ce.Details = edetail + } + if eclass := jsonutils.GetAnyString(jrbody2, []string{"title", "type", "error_code"}); len(eclass) > 0 { + ce.Class = eclass + } + return nil, nil, &ce } } diff --git a/pkg/util/httputils/httputils_test.go b/pkg/util/httputils/httputils_test.go new file mode 100644 index 0000000000..a9957a1abc --- /dev/null +++ b/pkg/util/httputils/httputils_test.go @@ -0,0 +1,113 @@ +package httputils + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "yunion.io/x/jsonutils" +) + +type SErrorMsg struct { + statusCode int + Code string + Message string + Title string + Type string + ErrorCode string + ErrorMsg string + result JSONClientErrorMsg +} + +func TestError(t *testing.T) { + + for testName, msg := range map[string]SErrorMsg{ + "test1": { + statusCode: 400, + Code: "VPC.0601", + Message: "Securitygroup id is invalid.", + Title: "", + Type: "", + result: JSONClientErrorMsg{ + Error: &JSONClientError{ + Code: 400, + Class: "VPC.0601", + Details: "Securitygroup id is invalid.", + }, + }, + }, + "test2": { + statusCode: 400, + Code: "3435", + Message: "Securitygroup id is invalid.", + Title: "", + Type: "", + result: JSONClientErrorMsg{ + Error: &JSONClientError{ + Code: 3435, + Class: "", + Details: "Securitygroup id is invalid.", + }, + }, + }, + "test3": { + statusCode: 400, + ErrorCode: "APIGW.0301", + ErrorMsg: "Incorrect IAM authentication information: verify aksk signature fail", + result: JSONClientErrorMsg{ + Error: &JSONClientError{ + Code: 400, + Class: "APIGW.0301", + Details: "Incorrect IAM authentication information: verify aksk signature fail", + }, + }, + }, + } { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(msg.statusCode) + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(jsonutils.Marshal(msg).String())) + })) + _, _, err := JSONRequest(ts.Client(), context.Background(), THttpMethod("GET"), ts.URL, nil, nil, true) + if err != nil { + respErr := JSONClientErrorMsg{} + if err := json.Unmarshal([]byte(err.Error()), &respErr); err != nil { + t.Error(err) + } + if msg.result.Error.Class != respErr.Error.Class { + t.Errorf("expect %s class %s not %s", testName, msg.result.Error.Class, respErr.Error.Class) + } + if msg.result.Error.Code != respErr.Error.Code { + t.Errorf("expect %s code %d not %d", testName, msg.result.Error.Code, respErr.Error.Code) + } + if msg.result.Error.Details != respErr.Error.Details { + t.Errorf("expect %s detail %s not %s", testName, msg.result.Error.Class, respErr.Error.Class) + } + } + + ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(msg.statusCode) + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(jsonutils.Marshal(map[string]SErrorMsg{"error": msg}).String())) + })) + _, _, err = JSONRequest(ts.Client(), context.Background(), THttpMethod("GET"), ts.URL, nil, nil, true) + if err != nil { + respErr := JSONClientErrorMsg{} + if err := json.Unmarshal([]byte(err.Error()), &respErr); err != nil { + t.Error(err) + } + if msg.result.Error.Class != respErr.Error.Class { + t.Errorf("expect %s class %s not %s", testName, msg.result.Error.Class, respErr.Error.Class) + } + if msg.result.Error.Code != respErr.Error.Code { + t.Errorf("expect %s code %d not %d", testName, msg.result.Error.Code, respErr.Error.Code) + } + if msg.result.Error.Details != respErr.Error.Details { + t.Errorf("expect %s detail %s not %s", testName, msg.result.Error.Class, respErr.Error.Class) + } + } + + } +}