diff --git a/pkg/httperrors/errors.go b/pkg/httperrors/errors.go index 1891b64131..cc252d37e6 100644 --- a/pkg/httperrors/errors.go +++ b/pkg/httperrors/errors.go @@ -1,8 +1,8 @@ package httperrors import ( + "bytes" "fmt" - "yunion.io/x/onecloud/pkg/util/httputils" ) @@ -11,13 +11,55 @@ func NewJsonClientError(code int, title string, msg string, error httputils.Erro return &err } +func msgToTemplate(msg string) string { + // 将%s %d之类格式化字符串转换成{0}、{1}格式 + // 注意: 1.不支持复杂类型的转换例如%.2f , %[1]d, % x + // 2.原始msg中如果包含{0},{1}形式的字符串同样会引发错误。 + // 在抛出error msg时应注意避免 + fmtstr := false + lst := []rune(msg) + lastIndex := len(lst) - 1 + temp := bytes.Buffer{} + index := 0 + for i, c := range lst { + switch c { + case '%': + if fmtstr || i == lastIndex { + temp.WriteRune(c) + fmtstr = false + } else { + fmtstr = true + } + case 'v', 'T', 't', 'b', 'c', 'd', 'o', 'q', 'x', 'X', 'U', 'e', 'E', 'f', 'F', 'g', 'G', 's', 'p': + if fmtstr { + temp.WriteRune('{') + temp.WriteString(fmt.Sprintf("%d", index)) + temp.WriteRune('}') + index++ + fmtstr = false + } else { + temp.WriteRune(c) + } + + default: + if fmtstr { + temp.WriteRune('%') + } + temp.WriteRune(c) + fmtstr = false + } + } + + return temp.String() +} + func errorMessage(msg string, params ...interface{}) (string, httputils.Error) { fields := make([]string, len(params)) for i, v := range params { fields[i] = fmt.Sprint(v) } - error := httputils.Error{Id: msg, Fields: fields} + error := httputils.Error{Id: msgToTemplate(msg), Fields: fields} if len(params) > 0 { msg = fmt.Sprintf(msg, params...) } diff --git a/pkg/httperrors/errors_test.go b/pkg/httperrors/errors_test.go index 051bc74d9a..a126b7fc71 100644 --- a/pkg/httperrors/errors_test.go +++ b/pkg/httperrors/errors_test.go @@ -50,3 +50,37 @@ func TestVariadic(t *testing.T) { }) } } + +func TestMsgToTemplate(t *testing.T) { + cases := []struct { + name string + msg string + params []interface{} + out string + }{ + { + name: "non-empty msg to template", + msg: "%% baremetals %s delete.time %d%", + out: "% baremetals {0} delete.time {1}%", + }, + { + name: "empty msg to template", + msg: "", + out: "", + }, + { + name: "non-empty with zh-utf8 characters msg to template", + msg: "%% baremetals %s 中文%d ¥%%", + out: "% baremetals {0} 中文{1} ¥%", + }, + } + + for _, c := range cases{ + t.Run(c.name, func(t *testing.T) { + resp := msgToTemplate(c.msg) + if resp != c.out { + t.Errorf("want %s, got %s", c.out, resp) + } + }) + } +} \ No newline at end of file