github.com/vicanso/hes@v0.7.0/http_errors.go (about) 1 package hes 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "net/http" 8 "runtime" 9 "strings" 10 "sync" 11 ) 12 13 const ( 14 defaultStatusCode = http.StatusBadRequest 15 ) 16 17 var callerEnabled = false 18 19 type ( 20 // Error http error 21 Error struct { 22 lock *sync.RWMutex 23 // http status code 24 StatusCode int `json:"statusCode,omitempty"` 25 // error code 26 Code string `json:"code,omitempty"` 27 // category 28 Category string `json:"category,omitempty"` 29 // sub category 30 SubCategory string `json:"subCategory,omitempty"` 31 // title 32 Title string `json:"title,omitempty"` 33 // message 34 Message string `json:"message,omitempty"` 35 // exception error 36 Exception bool `json:"exception,omitempty"` 37 // original error 38 Err error `json:"-"` 39 // File caller file 40 File string `json:"file,omitempty"` 41 // Line caller line 42 Line int `json:"line,omitempty"` 43 // extra info for error 44 Extra map[string]interface{} `json:"extra,omitempty"` 45 // sub errors 46 Errs []*Error `json:"errs,omitempty"` 47 } 48 FileConvertor func(file string) string 49 ) 50 51 // EnableCaller enable caller 52 func EnableCaller(enabled bool) { 53 callerEnabled = enabled 54 } 55 56 // fileConvertor file convertor 57 var fileConvertor FileConvertor 58 59 // SetFileConvertor set file convertor 60 func SetFileConvertor(fn FileConvertor) { 61 fileConvertor = fn 62 } 63 64 // Error error interface 65 func (e *Error) Error() string { 66 str := fmt.Sprintf("message=%s", e.Message) 67 68 if e.Code != "" { 69 str = fmt.Sprintf("code=%s, %s", e.Code, str) 70 } 71 72 if e.Category != "" { 73 str = fmt.Sprintf("category=%s, %s", e.Category, str) 74 } 75 76 if e.StatusCode != 0 { 77 str = fmt.Sprintf("statusCode=%d, %s", e.StatusCode, str) 78 } 79 80 if e.File != "" { 81 str = fmt.Sprintf("file=%s, line=%d, %s", e.File, e.Line, str) 82 } 83 if len(e.Errs) != 0 { 84 arr := make([]string, len(e.Errs)) 85 for index, err := range e.Errs { 86 arr[index] = err.Error() 87 } 88 str = fmt.Sprintf("%s, errs:(%s)", str, strings.Join(arr, ",")) 89 } 90 91 return str 92 } 93 94 // Format error format 95 func (e *Error) Format(s fmt.State, verb rune) { 96 switch verb { 97 default: 98 fallthrough 99 case 's': 100 _, _ = io.WriteString(s, e.Error()) 101 case 'q': 102 fmt.Fprintf(s, "%q", e.Message) 103 } 104 } 105 106 // SetCaller set info of caller 107 func (e *Error) SetCaller(skip int) { 108 if e.lock != nil { 109 e.lock.Lock() 110 defer e.lock.Unlock() 111 } 112 _, file, line, _ := runtime.Caller(skip) 113 if fileConvertor != nil { 114 file = fileConvertor(file) 115 } 116 e.File = file 117 e.Line = line 118 } 119 120 // ToJSON error to json 121 func (e *Error) ToJSON() []byte { 122 buf, _ := json.Marshal(e) 123 return buf 124 } 125 126 // CloneWithMessage clone error and update message 127 func (e *Error) CloneWithMessage(message string) *Error { 128 clone := e.Clone() 129 clone.Message = message 130 return clone 131 } 132 133 // IsEmpty check the error list is empty 134 func (e *Error) IsEmpty() bool { 135 if e.lock != nil { 136 e.lock.RLock() 137 defer e.lock.RUnlock() 138 } 139 return len(e.Errs) == 0 140 } 141 142 // IsNotEmpty check the error list is not empty 143 func (e *Error) IsNotEmpty() bool { 144 // is empty有判断锁,因此不需要判断 145 return !e.IsEmpty() 146 } 147 148 // AddExtra add extra value to error 149 func (e *Error) AddExtra(key string, value interface{}) { 150 if e.lock != nil { 151 e.lock.Lock() 152 defer e.lock.Unlock() 153 } 154 if e.Extra == nil { 155 e.Extra = make(map[string]interface{}) 156 } 157 e.Extra[key] = value 158 } 159 160 // Exists return true if it already exists 161 func (e *Error) exists(he *Error) bool { 162 for _, item := range e.Errs { 163 if he.Title == item.Title && 164 he.Message == item.Message && 165 he.Category == item.Category { 166 return true 167 } 168 } 169 return false 170 } 171 func (e *Error) add(errs ...error) { 172 if len(e.Errs) == 0 { 173 e.Errs = make([]*Error, 0) 174 } 175 for _, err := range errs { 176 if err == nil { 177 continue 178 } 179 he := Wrap(err) 180 // 如果包括子错误,则直接添加子错误列表 181 if he.IsNotEmpty() { 182 for _, err := range he.Errs { 183 e.add(err) 184 } 185 continue 186 } 187 // 判断是否已存在相同的error 188 // 如果已有相同error,则不添加 189 if e.exists(he) { 190 continue 191 } 192 193 e.Errs = append(e.Errs, he) 194 } 195 } 196 197 // Add add error to error list 198 func (e *Error) Add(errs ...error) { 199 if len(errs) == 0 { 200 return 201 } 202 if e.lock != nil { 203 e.lock.Lock() 204 defer e.lock.Unlock() 205 } 206 e.add(errs...) 207 } 208 209 // Clone clone error 210 func (e *Error) Clone() *Error { 211 he := new(Error) 212 *he = *e 213 if he.lock != nil { 214 he.lock = &sync.RWMutex{} 215 } 216 return he 217 } 218 219 func newError(message string, statusCode, skip int, category ...string) *Error { 220 he := &Error{ 221 Message: message, 222 StatusCode: statusCode, 223 } 224 if len(category) != 0 { 225 he.Category = category[0] 226 } 227 if callerEnabled { 228 he.SetCaller(skip) 229 } 230 return he 231 } 232 233 // New create a http error 234 func New(message string, category ...string) *Error { 235 he := newError(message, defaultStatusCode, 3, category...) 236 return he 237 } 238 239 // NewMutex create a http error with mutex 240 func NewMutex(message string, category ...string) *Error { 241 he := New(message, category...) 242 he.lock = &sync.RWMutex{} 243 return he 244 } 245 246 // NewWithStatusCode create a http error with status code 247 func NewWithStatusCode(message string, statusCode int, category ...string) *Error { 248 he := newError(message, statusCode, 3, category...) 249 return he 250 } 251 252 // NewWithError create a http error with error 253 func NewWithError(err error) *Error { 254 he := newError(err.Error(), defaultStatusCode, 3) 255 he.Err = err 256 return he 257 } 258 259 // NewWithErrorStatusCode create a http error with error and status code 260 func NewWithErrorStatusCode(err error, statusCode int) *Error { 261 he := newError(err.Error(), statusCode, 3) 262 he.Err = err 263 return he 264 } 265 266 // NewWithCaller create a http error with caller 267 func NewWithCaller(message string) *Error { 268 he := &Error{ 269 Message: message, 270 StatusCode: defaultStatusCode, 271 } 272 he.SetCaller(2) 273 return he 274 } 275 276 // NewWithException create a http error and set exception to true 277 func NewWithException(message string) *Error { 278 he := newError(message, defaultStatusCode, 3) 279 he.Exception = true 280 return he 281 } 282 283 // IsError check the error whether or not hes error 284 func IsError(err error) bool { 285 _, ok := err.(*Error) 286 return ok 287 } 288 289 // Wrap wrap error 290 func Wrap(err error) *Error { 291 he, ok := err.(*Error) 292 if ok { 293 return he.Clone() 294 } 295 he = newError(err.Error(), defaultStatusCode, 3) 296 he.Err = err 297 return he 298 }