golang封装业务err(结合iris)

2024-01-28 20:44

本文主要是介绍golang封装业务err(结合iris),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

golang封装业务err

我们有时在web开发时,仅凭httpStatus以及msg是不方便维护和体现我们的业务逻辑的。所以就需要封装我们自己的业务错误。

  • 自定义biz_err
  • 维护err map:errorResponseMap、errorHttpStatusMap

注意:本文主要以演示为主,主要是让大家熟悉封装自定义错误的思路,故而封装的较为简单。大家可根据自己公司需求来进行拓展。

代码仓库地址:https://github.com/ziyifast/ziyifast-code_instruction

项目结构:
在这里插入图片描述

1 err:自定义err,重写打印格式等

1.1 biz_err_demo/error/zerr/errors.go:new方法

  1. 重写控制台打印格式
  2. 封装new方法
  • DefaultBizWrap:不含原始err
  • BizWrap:包含原始err
package zerrimport ("errors""fmt""io"
)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 ("fmt""io""path""runtime""strings"
)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[i+1:]i = strings.Index(name, ".")return name[i+1:]
}

1.3 biz_err_demo/error/zerr/wrap.go:判断err类型

由自定义err,判断是否属于某个err

package zerrimport ("errors""reflect"
)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/zerr""net/http""strings"
)const (Undefined                 = "Undefined"OsCreateFileError         = "OsCreateFileError"ImageNotSupported         = "ImageNotSupported"UsernameOrPasswordInValid = "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/json"ContentTypeXml  = "application/xml"
)

2.2 biz_err_demo/controller/base_controller.go

package controllerimport ("encoding/json""encoding/xml""github.com/kataras/iris/v12""github.com/kataras/iris/v12/mvc""github.com/sirupsen/logrus""myTest/demo_home/biz_err_demo/constant""myTest/demo_home/biz_err_demo/error/biz_err""myTest/demo_home/biz_err_demo/response""net/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 ("errors""github.com/kataras/iris/v12/mvc""myTest/demo_home/biz_err_demo/error/biz_err""myTest/demo_home/biz_err_demo/error/zerr""myTest/demo_home/biz_err_demo/response""net/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/json""github.com/kataras/iris/v12/mvc""github.com/sirupsen/logrus""myTest/demo_home/biz_err_demo/constant""myTest/demo_home/biz_err_demo/error/biz_err"
)type Code stringtype JsonResponse struct {Code    Code        `json:"code"`Msg     string      `json:"msg"`Content 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 ("errors""github.com/sirupsen/logrus""myTest/demo_home/biz_err_demo/error/biz_err""myTest/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/v12""github.com/kataras/iris/v12/mvc""myTest/demo_home/biz_err_demo/controller"
)func main() {app := iris.New()mvc.New(app).Handle(new(controller.TestBizController))app.Listen(":8088", nil)
}

在这里插入图片描述

这样前端就能直接根据我们的业务错误码展示对应msg信息

这篇关于golang封装业务err(结合iris)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/654727

相关文章

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

业务协同平台--简介

一、使用场景         1.多个系统统一在业务协同平台定义协同策略,由业务协同平台代替人工完成一系列的单据录入         2.同时业务协同平台将执行任务推送给pda、pad等执行终端,通知各人员、设备进行作业执行         3.作业过程中,可设置完成时间预警、作业节点通知,时刻了解作业进程         4.做完再给你做过程分析,给出优化建议         就问你这一套下

JavaSE——封装、继承和多态

1. 封装 1.1 概念      面向对象程序三大特性:封装、继承、多态 。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节 。     比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器, USB 插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU 、显卡、内存等一些硬件元件。

哈希表的封装和位图

文章目录 2 封装2.1 基础框架2.2 迭代器(1)2.3 迭代器(2) 3. 位图3.1 问题引入3.2 左移和右移?3.3 位图的实现3.4 位图的题目3.5 位图的应用 2 封装 2.1 基础框架 文章 有了前面map和set封装的经验,容易写出下面的代码 // UnorderedSet.h#pragma once#include "HashTable.h"

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理 秒杀系统是应对高并发、高压力下的典型业务场景,涉及到并发控制、库存管理、事务管理等多个关键技术点。本文将深入剖析秒杀商品业务中常见的几个核心问题,包括 AOP 事务管理、同步锁机制、乐观锁、CAS 操作,以及用户限购策略。通过这些技术的结合,确保秒杀系统在高并发场景下的稳定性和一致性。 1. AOP 代理对象与事务管理 在秒杀商品

封装MySQL操作时Where条件语句的组织

在对数据库进行封装的过程中,条件语句应该是相对难以处理的,毕竟条件语句太过于多样性。 条件语句大致分为以下几种: 1、单一条件,比如:where id = 1; 2、多个条件,相互间关系统一。比如:where id > 10 and age > 20 and score < 60; 3、多个条件,相互间关系不统一。比如:where (id > 10 OR age > 20) AND sco

Golang进程权限调度包runtime

关于 runtime 包几个方法: Gosched:让当前线程让出 cpu 以让其它线程运行,它不会挂起当前线程,因此当前线程未来会继续执行GOMAXPROCS:设置最大的可同时使用的 CPU 核数Goexit:退出当前 goroutine(但是defer语句会照常执行)NumGoroutine:返回正在执行和排队的任务总数GOOS:目标操作系统NumCPU:返回当前系统的 CPU 核数量 p

Golang 网络爬虫框架gocolly/colly(五)

gcocolly+goquery可以非常好地抓取HTML页面中的数据,但碰到页面是由Javascript动态生成时,用goquery就显得捉襟见肘了。解决方法有很多种: 一,最笨拙但有效的方法是字符串处理,go语言string底层对应字节数组,复制任何长度的字符串的开销都很低廉,搜索性能比较高; 二,利用正则表达式,要提取的数据往往有明显的特征,所以正则表达式写起来比较简单,不必非常严谨; 三,使

Golang网络爬虫框架gocolly/colly(四)

爬虫靠演技,表演得越像浏览器,抓取数据越容易,这是我多年爬虫经验的感悟。回顾下个人的爬虫经历,共分三个阶段:第一阶段,09年左右开始接触爬虫,那时由于项目需要,要访问各大国际社交网站,Facebook,myspace,filcker,youtube等等,国际上叫得上名字的社交网站都爬过,大部分网站提供restful api,有些功能没有api,就只能用http抓包工具分析协议,自己爬;国内的优酷、

Golang网络爬虫框架gocolly/colly(三)

熟悉了《Golang 网络爬虫框架gocolly/colly 一》和《Golang 网络爬虫框架gocolly/colly 二》之后就可以在网络上爬取大部分数据了。本文接下来将爬取中证指数有限公司提供的行业市盈率。(http://www.csindex.com.cn/zh-CN/downloads/industry-price-earnings-ratio) 定义数据结构体: type Zhj