Go项目实战-让自定义Error支持Go的errors.Is判定以及原型模式的应用

embedded/2025/1/22 15:52:26/

经过前面三节高代码强度的学习,相信大家都已经有点累了,本节我们不着急继续“赶路”,休息片刻!我们换个轻松点的话题,聊一聊咱们项目定制化Error--AppError 怎么支持Go语言的 errors.Is 判定,以及项目预定义的那些Error在实际使用过程中某些情况下会出现循环引用的问题,我们会利用一个原型设计模式来解决这个问题。

a2570140a754181d7836ab3ecb49fd13.png

项目定制化Error 回顾

定义项目 Error 实现错误链和发生位置记录这篇文章中我们给项目定义了自己的Error类型 AppError

type AppError struct {code  int    `json:"code"`msg   string `json:"msg"`cause error  `json:"cause"`occurred string `json:"occurred"`
}

目的是为了通过自己的封装让Go的Error能支持错误原因和发生位置的记录。同时我们还为项目的开发预定义了很多Error变量。

// 用户模块相关错误码 10000100 ~ 1000199
var (ErrUserInvalid      = newError(10000101, "用户异常")ErrUserNameOccupied = newError(10000102, "用户名已被占用")ErrUserNotRight     = newError(10000103, "用户名或密码不正确")
)
// 商品模块相关错误码 10000200 ~ 1000299
var (ErrCommodityNotExists = newError(10000200, "商品不存在")ErrCommodityStockOut  = newError(10000201, "库存不足")
)
// 购物车模块相关错误码 10000300 ~ 1000399
var (ErrCartItemParam = newError(10000300, "购物项参数异常")ErrCartWrongUser = newError(10000301, "用户购物信息不匹配")
)

在 Go项目Error的统一管理和处理建议 中我建议需要返回给客户端返回约定好的错误码的错误都在这里预先定义。而对于一些像DB、存储之类错误产生的Error 无需告知客户端明确原因只需要让客户端发生了服务端内部错误的情况、或者不知道怎么处理的底层错误,我们先统一用Wrap方法把它包装成应用的AppError。

err = DBMaster().WithContext(ud.ctx).Create(userModel).Error
if err != nil {err = errcode.Wrap("UserDaoCreateUserError", err)return nil, err
}

在设计的过程中,觉得已经够全面了,但是只要真正把它来开发需求时,还是能发现有问题的,具体什么问题呢? 我们继续往下看。

怎么让自定义Error支持Go的errors.Is判定

想让Error支持Go的errors.Is 判定,我们先来看看errors.Is 方法里到底是怎么判定Error是不是给定的目标错误的,其源码如下:

func Is(err, target error) bool {if target == nil {return err == target}isComparable := reflectlite.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}}
}

其源码中逻辑是会一层层地解包error,每层的error都去看看是否已经实现了 interface{ Is(error) bool } 这个接口,如果实现了调用该层error的Is方法进行判定,如果跟给定error不相等或者没有实现Is接口,则继续用Uwrap解包,解到不能解为止 (err == nil)。

所以这里给我们预留了两个接口,我们让AppError实现 Is 和 Uwrap 方法后,它也就支持Go的 errors.Is 判定啦。

下面我们在 common/errcode/error.go 中加入这两个方法的实现。

package errcodefunc (e *AppError) UnWrap() error {return e.cause
}// Is 与上面的UnWrap一起让 *AppError 支持 errors.Is(err, target)
func (e *AppError) Is(target error) bool {targetErr, ok := target.(*AppError)if !ok {return false}return targetErr.Code() == e.Code()
}

关于项目自定义Error的优化,在课程中我还使用了这里使用设计模式里的原型模式, 把项目预定义的全局错误都是当作原型-prototype,保证我们既能规范管理我们项目的错误码,也能更自由放心地在程序中使用它们。

相关内容和详尽的实现代码,都涵盖在专栏中,现在扫码订阅专栏,一起加入学习吧

24c0dc5c9d61b0eafa6d0d9434db3ef2.png

本专栏分为五大部分,大部分内容已经更新完成

a92bd5d99d3a69e27af4f3db8c3bd15b.png
  • 第一部分介绍让框架变得好用的诸多实战技巧,比如通过自定义日志门面让项目日志更简单易用、支持自动记录请求的追踪信息和程序位置信息、通过自定义Error在实现Go error接口的同时支持给给错误添加错误链,方便追溯错误源头。

  • 第二部分:讲解项目分层架构的设计和划分业务模块的方法和标准,让你以后无论遇到什么项目都能按这套标准自己划分出模块和逻辑分层。后面几个部分均是该部分所讲内容的实践。

  • 第三部分:设计实现一个套支持多平台登录,Token泄露检测、同平台多设备登录互踢功能的用户认证体系,这套用户认证体系既可以在你未来开发产品时直接应用

  • 第四部分:商城app C端接口功能的实现,强化分层架构实现的讲解,这里还会讲解用责任链、策略和模版等设计模式去解决订单结算促销、支付方式支付场景等多种多样的实际问题。

  • 第五部分:单元测试、项目Docker镜像、K8s部署和服务保障相关的一些基础内容和注意事项

扫描上方二维码或者访问 https://xiaobot.net/p/golang 即刻订阅


http://www.ppmy.cn/embedded/156087.html

相关文章

c# PDF文件合并工具

界面 主要用于发票PDF文件的合并。经常出差要报销的很有用。 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System…

ucharts 使用

1.使用 ucharts 在微信小程序中 层级过高 导致布局错乱 添加&#xff1a;canvas2d true 发布到线上即可 开启后本地看到的话会混乱 但线上没问题 我看百度上搜的 说再加一个canvasId"myCanvasId" 要随便写一个 id <qiun-data-chartstype"ring":opts&…

【Nacos】健康检查机制(附实操案例)

目录 Nacos的两种健康检查机制服务实例类型注意事项实操演示 Nacos的两种健康检查机制 Nacos作为一个服务注册中心&#xff0c;需要感知服务的状态&#xff0c;才能为服务调用方提供良好的服务&#xff0c;而它自身提供了两种健康检查机制&#xff1a; 客户端主动上报机制&am…

【Postgres_Python】使用python脚本批量创建和导入多个PG数据库

之前批量创建和导入数据库分为2个python脚本进行&#xff0c;现整合优化代码合并为一个python脚本&#xff0c;可同步实现数据库的创建和数据导入。之前的文章链接&#xff1a; 【Postgres_Python】使用python脚本批量创建PG数据库 【Postgres_Python】使用python脚本将多个.S…

【电视盒子】HI3798MV300刷机教程笔记/备份遥控码修复遥控器/ADB/线刷卡刷/电视盒子安装第三方应用软件

心血来潮&#xff0c;看到电视机顶盒满天飞的广告&#xff0c;想改造一下家里的电视盒子&#xff0c;学一下网上的人刷机&#xff0c;但是一切都不知道怎么开始&#xff0c;虽然折腾了一天&#xff0c;以失败告终&#xff0c;还是做点刷机笔记。 0.我的机器 年少不会甄别&…

C++类型转换总结

类型转换 隐式转换 C自动执行很多类型转换&#xff1a; 将一种算术类型的值赋给另一种算术类型的变量时&#xff0c;C将对值进行转换&#xff1b; 表达式中包含不同的类型时&#xff0c;C将对值进行转换&#xff1b; 将参数传递给函数时&#xff0c;C将对值进行转换。 C类…

【BUUCTF】[RCTF2015]EasySQL1

二次注入原理 是一种比较隐蔽的 SQL 注入类型 用户输入的数据先被存储到数据库中&#xff08;此时可能未被恶意利用&#xff09;&#xff0c;后续应用程序从数据库中读取该数据并再次使用在 SQL 查询中&#xff0c;而此时就可能导致 SQL 注入问题。 如&#xff0c;用户注册时…

【Node.js]

一、概述 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境 &#xff0c;使用了一个事件驱动、非阻塞式I/O模型&#xff0c; 让JavaScript 运行在服务端的开发平台&#xff0c;它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。 官网地…