github.com/GuanceCloud/cliutils@v1.1.21/network/http/err.go (about) 1 // Unless explicitly stated otherwise all files in this repository are licensed 2 // under the MIT License. 3 // This product includes software developed at Guance Cloud (https://www.guance.com/). 4 // Copyright 2021-present Guance, Inc. 5 6 //nolint:golint,stylecheck 7 package http 8 9 import ( 10 "encoding/json" 11 "errors" 12 "fmt" 13 nhttp "net/http" 14 "strings" 15 16 "github.com/gin-gonic/gin" 17 ) 18 19 var ( 20 DefaultNamespace = "" 21 ErrUnexpectedInternalServerError = NewErr(errors.New(`unexpected internal server error`), nhttp.StatusInternalServerError) 22 ) 23 24 type HttpError struct { 25 ErrCode string `json:"error_code,omitempty"` 26 Err error `json:"-"` 27 HttpCode int `json:"-"` 28 } 29 30 type BodyResp struct { 31 *HttpError 32 Message string `json:"message,omitempty"` 33 Content interface{} `json:"content,omitempty"` 34 } 35 36 func NewNamespaceErr(err error, httpCode int, namespace string) *HttpError { 37 if err == nil { 38 return &HttpError{ 39 HttpCode: httpCode, 40 } 41 } else { 42 return &HttpError{ 43 ErrCode: titleErr(namespace, err), 44 HttpCode: httpCode, 45 Err: err, 46 } 47 } 48 } 49 50 func NewErr(err error, httpCode int) *HttpError { 51 return NewNamespaceErr(err, httpCode, DefaultNamespace) 52 } 53 54 func undefinedErr(err error) *HttpError { 55 return NewErr(err, nhttp.StatusInternalServerError) 56 } 57 58 func (he *HttpError) Error() string { 59 if he.Err == nil { 60 return "" 61 } else { 62 return he.Err.Error() 63 } 64 } 65 66 func (he *HttpError) HttpBodyPretty(c *gin.Context, body interface{}) { 67 if body == nil { 68 c.Status(he.HttpCode) 69 return 70 } 71 72 resp := &BodyResp{ 73 HttpError: he, 74 Content: body, 75 } 76 77 j, err := json.MarshalIndent(resp, "", " ") 78 if err != nil { 79 undefinedErr(err).httpRespf(c, "%s: %+#v", "json.Marshal() failed", resp) 80 return 81 } 82 83 c.Header("X-Content-Type-Options", "nosniff") 84 c.Data(he.HttpCode, `application/json`, j) 85 } 86 87 func (he *HttpError) WriteBody(c *gin.Context, obj interface{}) { 88 if obj == nil { 89 c.Status(he.HttpCode) 90 return 91 } 92 93 var bodyBytes []byte 94 var contentType string 95 var err error 96 97 switch x := obj.(type) { 98 case []byte: 99 bodyBytes = x 100 default: 101 contentType = `application/json` 102 103 bodyBytes, err = json.Marshal(obj) 104 if err != nil { 105 undefinedErr(err).httpRespf(c, "%s: %+#v", "json.Marshal() failed", obj) 106 return 107 } 108 } 109 110 c.Header("X-Content-Type-Options", "nosniff") 111 c.Data(he.HttpCode, contentType, bodyBytes) 112 } 113 114 type RawJSONBody []byte 115 116 // HttpBody Deprecated, use WriteBody. 117 func (he *HttpError) HttpBody(c *gin.Context, body interface{}) { 118 if body == nil { 119 c.Status(he.HttpCode) 120 return 121 } 122 123 var bodyBytes []byte 124 var contentType string 125 var err error 126 127 switch x := body.(type) { 128 case []byte: 129 bodyBytes = x 130 case RawJSONBody: 131 contentType = `application/json` 132 bodyBytes = x 133 default: 134 resp := &BodyResp{ 135 HttpError: he, 136 Content: body, 137 } 138 contentType = `application/json` 139 140 bodyBytes, err = json.Marshal(resp) 141 if err != nil { 142 undefinedErr(err).httpRespf(c, "%s: %+#v", "json.Marshal() failed", resp) 143 return 144 } 145 } 146 147 c.Header("X-Content-Type-Options", "nosniff") 148 c.Data(he.HttpCode, contentType, bodyBytes) 149 } 150 151 func HttpErr(c *gin.Context, err error) { 152 var ( 153 e1 *HttpError 154 e2 *MsgError 155 ) 156 157 switch { 158 case errors.As(err, &e1): 159 e1.httpRespf(c, "") 160 case errors.As(err, &e2): 161 e2.HttpError.httpRespf(c, e2.Fmt, e2.Args...) 162 default: 163 undefinedErr(err).httpRespf(c, "") 164 } 165 } 166 167 func HttpErrf(c *gin.Context, err error, format string, args ...interface{}) { 168 var ( 169 e1 *HttpError 170 e2 *MsgError 171 ) 172 173 switch { 174 case errors.As(err, &e1): 175 e1.httpRespf(c, format, args...) 176 case errors.As(err, &e2): 177 e2.HttpError.httpRespf(c, format, args...) 178 default: 179 undefinedErr(err).httpRespf(c, "") 180 } 181 } 182 183 func (he *HttpError) httpRespf(c *gin.Context, format string, args ...interface{}) { 184 resp := &BodyResp{ 185 HttpError: he, 186 } 187 188 resp.Message = fmt.Sprintf(format, args...) 189 190 j, err := json.Marshal(&resp) 191 if err != nil { 192 undefinedErr(err).httpRespf(c, "%s: %+#v", "json.Marshal() failed", resp) 193 return 194 } 195 196 c.Header("X-Content-Type-Options", "nosniff") 197 c.Data(he.HttpCode, `application/json`, j) 198 } 199 200 func titleErr(namespace string, err error) string { 201 if err == nil { 202 return "" 203 } 204 205 str := err.Error() 206 elem := strings.Split(str, ` `) 207 208 var out string 209 if namespace != "" { 210 out = namespace + `.` 211 } 212 213 for idx, e := range elem { 214 if idx == 0 { 215 out += e 216 continue 217 } 218 out += strings.Title(e) //nolint:staticcheck 219 } 220 221 return out 222 } 223 224 // Dynamic error create based on specific HttpError. 225 type MsgError struct { 226 *HttpError 227 Fmt string 228 Args []interface{} 229 } 230 231 func Errorf(he *HttpError, format string, args ...interface{}) *MsgError { 232 return &MsgError{ 233 HttpError: he, 234 Fmt: format, 235 Args: args, 236 } 237 } 238 239 func Error(he *HttpError, msg string) *MsgError { 240 return &MsgError{ 241 HttpError: he, 242 Fmt: "%s", 243 Args: []interface{}{msg}, 244 } 245 } 246 247 func (me *MsgError) Error() string { 248 if me.HttpError != nil { 249 return me.Err.Error() 250 } else { 251 return "" 252 } 253 }