仿团购网站模板,免签约收款WordPress,全面的网站建设,网络营销与推广golang封装业务err 我们有时在web开发时#xff0c;仅凭httpStatus以及msg是不方便维护和体现我们的业务逻辑的。所以就需要封装我们自己的业务错误。 自定义biz_err维护err map#xff1a;errorResponseMap、errorHttpStatusMap 注意#xff1a;本文主要以演示为主#xf…golang封装业务err 我们有时在web开发时仅凭httpStatus以及msg是不方便维护和体现我们的业务逻辑的。所以就需要封装我们自己的业务错误。 自定义biz_err维护err maperrorResponseMap、errorHttpStatusMap 注意本文主要以演示为主主要是让大家熟悉封装自定义错误的思路故而封装的较为简单。大家可根据自己公司需求来进行拓展。 代码仓库地址https://github.com/ziyifast/ziyifast-code_instruction
项目结构
1 err自定义err重写打印格式等
1.1 biz_err_demo/error/zerr/errors.gonew方法 重写控制台打印格式封装new方法 DefaultBizWrap不含原始errBizWrap包含原始err package zerrimport (errorsfmtio
)func New(message string) error {return fundamental{msg: message,stack: callers(),}
}func Errorf(format string, args ...interface{}) error {return fundamental{msg: fmt.Sprintf(format, args...),stack: callers(),}
}type fundamental struct {msg string*stack
}func (f *fundamental) Error() string { return f.msg }func (f *fundamental) Format(s fmt.State, verb rune) {switch verb {case v:if s.Flag() {io.WriteString(s, f.msg)f.stack.Format(s, verb)return}fallthroughcase s:io.WriteString(s, f.msg)case q:fmt.Fprintf(s, %q, f.msg)}
}func WithStack(err error) error {if err nil {return nil}return withStack{err,callers(),}
}type withStack struct {error*stack
}func (w *withStack) Cause() error { return w.error }func (w *withStack) Format(s fmt.State, verb rune) {switch verb {case v:if s.Flag() {fmt.Fprintf(s, %v, w.Cause())w.stack.Format(s, verb)return}fallthroughcase s:io.WriteString(s, w.Error())case q:fmt.Fprintf(s, %q, w.Error())}
}func Wrap(err error, message string) error {if err nil {return nil}err withMessage{cause: err,msg: message,}return withStack{err,callers(),}
}func Trace(err error) error {return Wrapf(err, )
}func Wrapf(err error, format string, args ...interface{}) error {if err nil {return nil}err withMessage{cause: err,msg: fmt.Sprintf(format, args...),}return withStack{err,callers(),}
}func WithMessage(err error, message string) error {if err nil {return nil}return withMessage{cause: err,msg: message,}
}func WithMessagef(err error, format string, args ...interface{}) error {if err nil {return nil}return withMessage{cause: err,msg: fmt.Sprintf(format, args...),}
}type withMessage struct {cause errormsg string
}func (w *withMessage) Error() string {return w.msg : w.cause.Error()
}func (w *withMessage) Cause() error {return w.cause
}func (w *withMessage) Format(s fmt.State, verb rune) {switch verb {case v:if s.Flag() {fmt.Fprintf(s, %v\n, w.Cause())io.WriteString(s, w.msg)return}fallthroughcase s, q:io.WriteString(s, w.Error())}
}func Cause(err error) error {type causer interface {Cause() error}for err ! nil {cause, ok : err.(causer)if !ok {break}err cause.Cause()}return err
}func WithCode(err error, code string) error {if err nil {return nil}return ErrWrap{cause: err,code: code,}
}func WithCodef(err error, format string, args ...interface{}) error {if err nil {return nil}return ErrWrap{cause: err,code: fmt.Sprintf(format, args...),}
}type ErrWrap struct {cause errorcode stringvars []string
}func (w *ErrWrap) Vars() []string {return w.vars
}func (w *ErrWrap) Code() string {return w.code
}func (w *ErrWrap) Error() string {var msg stringif w.cause ! nil {msg w.cause.Error()}return msg
}func (w *ErrWrap) Cause() error {return w.cause
}// Format rewrite format
func (w *ErrWrap) Format(s fmt.State, verb rune) {switch verb {case v:if s.Flag() {fmt.Fprintf(s, %v\n, w.Cause())io.WriteString(s, BizCode[string(w.code)])return}fallthroughcase s, q:io.WriteString(s, w.Error())}
}func BizWrap(err error, code string, message string, vars ...string) error {if err nil {return nil}codeErr : ErrWrap{cause: err,code: code,vars: vars,}err withMessage{cause: codeErr,msg: message,}return withStack{err,callers(),}
}func DefaultBizWrap(code string, vars ...string) error {err : errors.New()codeErr : ErrWrap{cause: err,code: code,vars: vars,}err withMessage{cause: codeErr,}return withStack{err,callers(),}
}1.2 biz_err_demo/error/zerr/stack.go堆栈打印格式 定义堆栈打印格式 package zerrimport (fmtiopathruntimestrings
)type Frame uintptrfunc (f Frame) pc() uintptr { return uintptr(f) - 1 }func (f Frame) file() string {fn : runtime.FuncForPC(f.pc())if fn nil {return unknown}file, _ : fn.FileLine(f.pc())return file
}func (f Frame) line() int {fn : runtime.FuncForPC(f.pc())if fn nil {return 0}_, line : fn.FileLine(f.pc())return line
}func (f Frame) Format(s fmt.State, verb rune) {switch verb {case s:switch {case s.Flag():pc : f.pc()fn : runtime.FuncForPC(pc)if fn nil {io.WriteString(s, unknown)} else {file, _ : fn.FileLine(pc)fmt.Fprintf(s, %s\n\t%s, fn.Name(), file)}default:io.WriteString(s, path.Base(f.file()))}case d:fmt.Fprintf(s, %d, f.line())case n:name : runtime.FuncForPC(f.pc()).Name()io.WriteString(s, funcname(name))case v:f.Format(s, s)io.WriteString(s, :)f.Format(s, d)}
}type StackTrace []Framefunc (st StackTrace) Format(s fmt.State, verb rune) {switch verb {case v:switch {case s.Flag():for _, f : range st {fmt.Fprintf(s, \n%v, f)}case s.Flag(#):fmt.Fprintf(s, %#v, []Frame(st))default:fmt.Fprintf(s, %v, []Frame(st))}case s:fmt.Fprintf(s, %s, []Frame(st))}
}type stack []uintptrfunc (s *stack) Format(st fmt.State, verb rune) {switch verb {case v:switch {case st.Flag():for _, pc : range *s {f : Frame(pc)fmt.Fprintf(st, \n%v, f)}}}
}func (s *stack) StackTrace() StackTrace {f : make([]Frame, len(*s))for i : 0; i len(f); i {f[i] Frame((*s)[i])}return f
}func callers() *stack {const depth 32var pcs [depth]uintptrn : runtime.Callers(3, pcs[:])if n 1 {n 1}var st stack pcs[0:n]return st
}func funcname(name string) string {i : strings.LastIndex(name, /)name name[i1:]i strings.Index(name, .)return name[i1:]
}
1.3 biz_err_demo/error/zerr/wrap.go判断err类型 由自定义err判断是否属于某个err package zerrimport (errorsreflect
)func Unwrap(err error) error {u, ok : err.(interface {Cause() error})if !ok {return errors.Unwrap(err)}return u.Cause()
}func Is(err, target error) bool {if target nil {return err target}isComparable : reflect.TypeOf(target).Comparable()for {if isComparable err target {return true}if x, ok : err.(interface{ Is(error) bool }); ok x.Is(target) {return true}if err Unwrap(err); err nil {return false}}
}func As(err error, target interface{}) bool {if target nil {panic(errors: target cannot be nil)}val : reflect.ValueOf(target)typ : val.Type()if typ.Kind() ! reflect.Ptr || val.IsNil() {panic(errors: target must be a non-nil pointer)}targetType : typ.Elem()if targetType.Kind() ! reflect.Interface !targetType.Implements(errorType) {panic(errors: *target must be interface or implement error)}for err ! nil {if reflect.TypeOf(err).AssignableTo(targetType) {val.Elem().Set(reflect.ValueOf(err))return true}if x, ok : err.(interface{ As(interface{}) bool }); ok x.As(target) {return true}err Unwrap(err)}return false
}var errorType reflect.TypeOf((*error)(nil)).Elem()1.4 biz_err_demo/error/biz_err/code.go业务错误码 自定义业务错误码、对应错误信息及对应错误对应的httpStatusCode package biz_errimport (myTest/demo_home/biz_err_demo/error/zerrnet/httpstrings
)const (Undefined UndefinedOsCreateFileError OsCreateFileErrorImageNotSupported ImageNotSupportedUsernameOrPasswordInValid UsernameOrPasswordInValid
)var errorResponseMap map[string]string{OsCreateFileError: 创建文件失败,ImageNotSupported: 图片格式不支持,UsernameOrPasswordInValid: 用户名或密码错误,
}var errorHttpStatusMap map[string]int{OsCreateFileError: http.StatusInternalServerError,ImageNotSupported: http.StatusInternalServerError,UsernameOrPasswordInValid: http.StatusInternalServerError,
}func ParseBizErr(err error) (httpStatus int, code, msg string) {if err nil {code Undefined}vars : make([]string, 0)errWrap : new(zerr.ErrWrap)var cause errorif as : zerr.As(err, errWrap); as {code errWrap.Code()cause errWrap.Cause()vars errWrap.Vars()} else {code Undefined}if code Undefined {var undefinedMsg stringif err ! nil {undefinedMsg err.Error()}if undefinedMsg || undefinedMsg : {undefinedMsg errorResponseMap[code]}return errorHttpStatusMap[code], code, undefinedMsg}if status, ok : errorHttpStatusMap[code]; ok {httpStatus status} else {httpStatus http.StatusOK}if bizMsg, ok : errorResponseMap[code]; ok {for _, v : range vars {bizMsg strings.Replace(bizMsg, %s, v, 1)}msg bizMsgif cause ! nil {_, _, causeMsg : ParseBizErr(cause)if causeMsg ! {msg , causeMsg} else {msg , errWrap.Error()}}} else {msg errWrap.Error()}return httpStatus, code, msg
}func ErrResponse(err error) (httpStatus int, code, msg string) {if err nil {code Undefined}vars : make([]string, 0)errWrap : new(zerr.ErrWrap)var cause errorif as : zerr.As(err, errWrap); as {code errWrap.Code()cause errWrap.Cause()vars errWrap.Vars()} else {code Undefined}if status, ok : errorHttpStatusMap[code]; ok {httpStatus status} else {httpStatus http.StatusOK}if bizMsg, ok : errorResponseMap[code]; ok {for _, v : range vars {bizMsg strings.Replace(bizMsg, %s, v, 1)}msg bizMsgif cause ! nil {_, _, causeMsg : ErrResponse(cause)if causeMsg ! {msg causeMsg} else {msg errWrap.Error()}}} else {msg errWrap.Error()}return httpStatus, code, msg
}2 controller封装base_controller
2.1 biz_err_demo/constant/constant.go
package constantconst (ContentTypeJson application/jsonContentTypeXml application/xml
)2.2 biz_err_demo/controller/base_controller.go
package controllerimport (encoding/jsonencoding/xmlgithub.com/kataras/iris/v12github.com/kataras/iris/v12/mvcgithub.com/sirupsen/logrusmyTest/demo_home/biz_err_demo/constantmyTest/demo_home/biz_err_demo/error/biz_errmyTest/demo_home/biz_err_demo/responsenet/http
)type BaseController struct {Ctx iris.Context
}func commonResp(errMsg string, httpCode int, returnCode response.Code, content interface{}) mvc.Response {payload : response.JsonResponse{Code: returnCode,Msg: errMsg,Content: content,}contentDetail, err : json.Marshal(payload)if err ! nil {logrus.Infof(marshal json response error %v, err)}return mvc.Response{Code: httpCode,Content: contentDetail,ContentType: constant.ContentTypeJson,}
}func (c *BaseController) Xml(httpCode int, content interface{}) mvc.Response {payload, err : xml.Marshal(content)if err ! nil {logrus.Errorf(marshal xml response error %v, err)}return c.XmlRaw(httpCode, payload)
}func (c *BaseController) XmlOK(content interface{}) mvc.Response {payload, err : xml.Marshal(content)if err ! nil {logrus.Errorf(marshal xml response error %v, err)}return c.XmlRaw(http.StatusOK, payload)
}func (c *BaseController) XmlRaw(httpCode int, content []byte) mvc.Response {return mvc.Response{Code: httpCode,Content: content,ContentType: constant.ContentTypeXml,}
}func (c *BaseController) JsonBizError(err error) mvc.Response {httpStatus, code, msg : biz_err.ErrResponse(err)return commonResp(msg, httpStatus, response.Code(code), nil)
}2.3 biz_err_demo/controller/test_biz_controller.go
package controllerimport (errorsgithub.com/kataras/iris/v12/mvcmyTest/demo_home/biz_err_demo/error/biz_errmyTest/demo_home/biz_err_demo/error/zerrmyTest/demo_home/biz_err_demo/responsenet/http
)type TestBizController struct {BaseController
}func (t *TestBizController) BeforeActivation(b mvc.BeforeActivation) {b.Handle(http.MethodGet, /testBizErr, TestBizErr)
}func (t *TestBizController) TestBizErr() mvc.Result {err1 : errors.New()err : zerr.BizWrap(err1, biz_err.UsernameOrPasswordInValid, )return response.JsonBizError(err)
}3 封装response
3.1 biz_err_demo/response/json_response.go
package responseimport (encoding/jsongithub.com/kataras/iris/v12/mvcgithub.com/sirupsen/logrusmyTest/demo_home/biz_err_demo/constantmyTest/demo_home/biz_err_demo/error/biz_err
)type Code stringtype JsonResponse struct {Code Code json:codeMsg string json:msgContent interface{} json:content,omitempty
}func JsonBizError(err error) mvc.Response {httpStatus, code, msg : biz_err.ErrResponse(err)return commonResp(msg, httpStatus, Code(code), nil)
}func commonResp(errMsg string, httpCode int, returnCode Code, content interface{}) mvc.Response {payload : JsonResponse{Code: returnCode,Msg: errMsg,Content: content,}contentDetail, err : json.Marshal(payload)if err ! nil {logrus.Errorf(%v, err)}return mvc.Response{Code: httpCode,Content: contentDetail,ContentType: constant.ContentTypeJson,}
}4 测试效果
4.1 biz_err_demo/test/main.go
package mainimport (errorsgithub.com/sirupsen/logrusmyTest/demo_home/biz_err_demo/error/biz_errmyTest/demo_home/biz_err_demo/error/zerr
)func init() {logrus.SetReportCaller(true) // 设置日志是否记录被调用的位置默认值为 false
}func main() {TestWithNoSourceErr()TestWithSourceErr()TestParseBizErr()
}func TestWithNoSourceErr() {err : zerr.DefaultBizWrap(biz_err.UsernameOrPasswordInValid, )logrus.Errorf(TestWithNoSourceErr %v, err)
}func TestWithSourceErr() {err : errors.New(invalid image)err zerr.BizWrap(err, biz_err.ImageNotSupported, )logrus.Errorf(TestWithSourceErr %v, err)
}func TestParseBizErr() {err : errors.New()err zerr.BizWrap(err, biz_err.ImageNotSupported, )httpStatus, bizCode, msg : biz_err.ParseBizErr(err)logrus.Errorf(httpStatus:%d bizCode:%s msg:%s, httpStatus, bizCode, msg)
}4.2 biz_err_demo/main.go
package mainimport (github.com/kataras/iris/v12github.com/kataras/iris/v12/mvcmyTest/demo_home/biz_err_demo/controller
)func main() {app : iris.New()mvc.New(app).Handle(new(controller.TestBizController))app.Listen(:8088, nil)
}这样前端就能直接根据我们的业务错误码展示对应msg信息