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信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/652761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp 用css animation做的鲤鱼跃龙门小游戏

第一次做这种小游戏&#xff0c;刚开始任务下来我心里是没底的&#xff0c;因为我就一个‘拍黄片’的&#xff0c;我那会玩前端的动画啊&#xff0c;后面尝试写了半天&#xff0c;当即我就给我领导说&#xff0c;你把我工资加上去&#xff0c;我一个星期给你做出来&#xff0c;…

Vulnhub靶场DC-9

攻击机192.168.223.128 靶机192.168.223.138 主机发现 nmap -sP 192.168.223.0/24 端口扫描 nmap -sV -p- -A 192.168.223.138 开启了22 80端口 访问一下web页面 有个查询界面 测试发现存在post型的sql注入 用sqlmap跑一下&#xff0c;因为是post型的&#xff0c;这里…

用于不对称卷积的验证参数的小程序

非对称卷积的特征图尺寸计算 此处只例举输入图像是正方形的情况。设输入图像尺寸为WxW&#xff0c;卷积核尺寸为ExF&#xff0c;步幅为S&#xff0c;Padding为P&#xff0c;卷积后的特征图尺寸为&#xff1a; 矩形卷积 如果输入图像是正方形&#xff0c;尺寸为WxW&#xff0c…

C++二叉搜索树详解

文章目录 1. 前言2. 二叉搜索树的概念3. 二叉搜索树的操作4. 二叉搜索树的实现5. 二叉搜索树的应用6. 二叉搜索树的性能分析 1. 前言 当涉及到组织和管理数据时&#xff0c;二叉搜索树是一种常用的数据结构。它不仅可以快速插入和删除元素&#xff0c;还可以高效地搜索和查找特…

Elasticsearch安装Head图形插件

一、Google浏览器扩展插件方式 1.安装插件 进入谷歌浏览器应用商店搜索“Elasticsearch Head”,点击链接跳转 点击“添加至Chrome”按钮安装即可。 2.使用插件 在浏览器的插件列表多了个一个放大镜图标 点击“New”新建链接,输入es节点或集群地址。 连接成功 可以进行概括…

windows CUDA更新(最简单方法)+虚拟环境torch和cuda安装

目录 一、Torch和对应cuda安装 1、查看本身电脑的cuda版本 2、查找对应cuda——torch——python版本 3、安装cuda 4、安装torch 二、window 10 NVIDIA cuda版本更新 一、Torch和对应cuda安装 项目使用torch想要使用GPU运行&#xff0c;但是报错&#xff0c;记录一下解决…

扭蛋机小程序开发:探索用户体验与商业价值的融合

一、引言 随着移动互联网的快速发展&#xff0c;小程序作为一种新型的应用形态&#xff0c;正逐渐改变着人们的生活方式。扭蛋机小程序便是其中一例&#xff0c;它结合了线上线下的互动体验&#xff0c;为用户带来了全新的娱乐方式。本文将探讨扭蛋机小程序的开发过程&#xf…

初见CodeQL

安装CodeQL CodeQL本身包含两部分解析引擎SDK 下载已经编译好的 CodeQL 执行程序 https://github.com/github/codeql-cli-binaries/releases 下载之后配置环境变量 安装 SDK CMD 进入 CodeQL 安装目录&#xff0c;使用 Git 安装 SDK git clone https://github.com/Semmle/ql安…

Vulnhub-dc6

信息收集 # nmap -sn 192.168.1.0/24 -oN live.port Starting Nmap 7.94 ( https://nmap.org ) at 2024-01-25 14:39 CST Nmap scan report for 192.168.1.1 Host is up (0.00075s latency). MAC Address: 00:50:56:C0:00:08 (VMware) Nmap scan report for 192.168.1.2…

JAVA多线程并发学习记录

基础知识 1.进程和线程 线程是最小的调度单位&#xff0c;进程是最小的资源分配单位 进程&#xff1a;当程序从磁盘加载到内存中这时就开启了一个进程&#xff0c;进程可视为程序的一个实例。大部分程序可以同时运行多个实例。 线程&#xff1a;线程是进程的一个子集&#…

机器学习第一个项目-----鸢尾花数据集加载及报错解决

项目步骤 如刚开始做&#xff0c;从 “项目开始” 看&#xff1b; 如遇到问题从 “问题” 开始看&#xff1b; 问题 报错如下 ModuleNotFoundError: No module named sklearn解决过程 查看官网&#xff0c;感觉可能是python版本和skilearn版本不匹配&#xff0c;更新一下p…

Spring MVC 请求流程

SpringMVC 请求流程 一、DispatcherServlet 是一个 Servlet二、Spring MVC 的完整请求流程 Spring MVC 框架是基于 Servlet 技术的。以请求为驱动&#xff0c;围绕 Servlet 设计的。Spring MVC 处理用户请求与访问一个 Servlet 是类似的&#xff0c;请求发送给 Servlet&#xf…

【vue3】Vue3 + Vite 项目搭建

Vue3 Vite 项目搭建 创建项目添加Vue Router 4路由配置添加Vant UI 组件库移动端rem适配添加iconfont字体图标库二次封装Axios请求库添加CSS预处理器Less添加全局状态管理插件Vuex 1.创建项目 Vite方式 1.1 进入开发目录, 执行指令创建新项目 更行node版本18 npm 7.x版本 su…

ThinkPhp3.2(qidian)部署文档

宝塔环境部署 申请域名以及域名解析 具体配置&#xff0c;可百度之 在宝塔面板中创建网站 上传代码导入数据配置运行目录 注意&#xff1a;&#xff08;如果版本&#xff1a;thinkphp3.2 &#xff09;配置 运行目录要特别注意&#xff1a;运行目录要选择根目录“/”&#xff…

什么是数据库的三级模式两级映象?

三级模式两级映象结构图 概念 三级模式 内模式&#xff1a;也称为存储模式&#xff0c;是数据物理结构和存储方式的描述&#xff0c;是数据在数据库内部的表示方式。定义所有的内部记录类型、索引和文件组织方式&#xff0c;以及数据控制方面的细节。模式&#xff1a;又称概念…

计算机今年炸了,究竟炸到什么程度呢❓

小兄弟&#xff0c;计算机哪年不爆炸啊&#xff01; 尤其是19年&#xff0c;20年&#xff0c;21年&#xff0c;可以说是计算机最卷的几年&#xff0c;这几年也刚好是互联网企业风头正盛的几年 从这里大家可以看出来&#xff0c;任何一个行业都有他的周期&#xff0c;任何一个专…

中等题 ------ 数组以及字符串

以前刷的都是一些简单题&#xff0c;从一些基本的数据结构到算法&#xff0c;得有400多道了&#xff0c;简单题就先这样吧&#xff0c;从今天以后就开始着手中等题和困难题了。 做了一些中等题&#xff0c;感觉确实和简单题没法比&#xff0c;简单题有些直接模拟&#xff0c;暴…

vue3框架基本使用

一、安装包管理工具 vite和vue-cli一样&#xff0c;都是脚手架。 1.node版本 PS E:\vuecode\vite1> node -v v18.12.12.安装yarn工具 2.1 yarn简单介绍 yarn是一个包管理工具&#xff0c;也是一个构建、打包工具 yarn需要借助npm进行安装&#xff1a;执行的命令行npm i…

linux安装docker-compose

前言 如果你的docker版本是23&#xff0c;请移步到linux安装新版docker&#xff08;23&#xff09;和docker-compose这篇博客 查看docker版本命令&#xff1a; docker --version今天安装docker-compose的时候&#xff0c;找了很多教程&#xff0c;但是本地一直报错&#xff0…

c++学习第十三讲---STL常用容器---string容器

string容器&#xff1a; 一、string的本质&#xff1a; string和char*的区别&#xff1a; char*是一个指针 string是一个类&#xff0c;封装了char*&#xff0c;管理这个字符串&#xff0c;是char*的容器。 二、string构造函数&#xff1a; string() ; …