写点什么

从零开发区块链应用 (四)-- 自定义业务错误信息

  • 2022 年 1 月 18 日
  • 本文字数:2324 字

    阅读完需:约 8 分钟

一、如何自定义错误信息

1.1 为什么要自定义自己的错误信息

在实际开发中引入错误码有如下好处:


  • 可以非常方便地定位问题和定位代码行(看到错误码知道什么意思,通过错误码可以定位到错误码所在行);

  • 如果 API 对外开放,有错误码将会更专业;

  • 错误码包含一定的信息,通过错误码可以判断出错误级别、错误模块和具体错误信息;

  • 在实际业务开发中,一个条错误信息需要包含两部分内容:直接展示给用户的 message 和用于开发人员 debug 的 error 。message 可能会直接展示给用户,error 是用于 debug 的错误信息,可能包含敏感/内部信息,不宜对外展示;

  • 业务开发过程中,可能需要判断错误是哪种类型以便做相应的逻辑处理,通过定制的错误码很容易做到这点;

  • Go 中的 HTTP 服务器开发都是引用 net/http 包,该包中只有 60 个错误码,基本都是跟 HTTP 请求相关的。在大型系统中,这些错误码完全不够用,而且跟业务没有任何关联,满足不了业务需求。

  • 在实际开发中,一个错误类型通常包含两部分:Code 部分,用来唯一标识一个错误;Message 部分,用来展示错误信息,这部分错误信息通常供前端直接展示。

1.2 错误码设计

一个良好结构的错误码有助于简化问题描述, 当前设计的错误码共有五位, 结构如下:



  • 第一位是服务级别, 1 为系统错误, 2 为普通错误。通常由用户非法操作引起;

  • 服务模块为两位数:一个大型系统的服务模块通常不超过两位数,如果超过,说明这个系统该拆分了;模块不是指 Go 中的模块, 而是指代某个范围, 比如数据库错误, 认证错误;

  • 错误码为两位数:防止一个模块定制过多的错误码,后期不好维护,一般是具体错误, 比如数据库错误中的插入错误, 找不到数据等。

  • code = 0 说明是正确返回,code > 0 说明是错误返回

  • 错误通常包括系统级错误码和服务级错误码

  • 建议代码中按服务模块将错误分类

  • 错误码均为 >= 0 的数

二、 实际开发错误处理

2.1 代码实现

在项目目录下新建一个 apicommon 目录, 并创建相应的模块。


package apicommon
import ( "github.com/gin-gonic/gin" "net/http")
type Response struct { Error *Error `json:"error,omitempty"` Msg string `json:"message"` Code int64 `json:"code"` Data interface{} `json:"result,omitempty"`}
type Error struct { Code int64 `json:"code"` // 错误码 Msg string `json:"message"` // 返回给用户的信息 Data string `json:"data,omitempty"` // 保存的内部错误信息}
// ReturnErrorResponse 返回错误func ReturnErrorResponse(ctx *gin.Context, errmsg string, code int64, err string) { response := &Response{ Error: &Error{ Msg: errmsg, Code: code, Data: err, }, } ctx.JSON(http.StatusOK, response) return}
复制代码

2.2 错误码实战

上面介绍了错误码的一些知识,这一部分讲开发中是如何使用 自定义错误函数来处理错误信息的。为了演示,我们新增一个根据手机号获取验证码的 API:


handler/router.go 中添加路由


func RouterStart() {
gin.SetMode(gin.ReleaseMode) //设置gin模式
router := gin.New()
personal := router.Group("/api/v2") { personal.POST("/sso/getAuthCode", handler.GetAuthCode) } logger.Info("API server start", "listen", config.Conf.Console.Port) router.Run(config.Conf.Console.Port)}
复制代码


handler 目录下增加业务处理函数 handler/personal.go


//手机号校验func VerifyMobileFormat(mobileNum string) bool {  regular := "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$"  reg := regexp.MustCompile(regular)  return reg.MatchString(mobileNum)}
// 根据手机号获取验证码func GetAuthCode(ctx *gin.Context) { phone := ctx.PostForm("telephone") logger.Info("GetAuthCodeRequest req:", "phone:", phone)
//判断手机号是否正确 res := VerifyMobileFormat(phone) if res == false { //如果错误,则返回:手机号错误信息 apicommon.ReturnErrorResponse(ctx, "手机号错误!", -1, "Wrong phone number")
} else { //如果正确,则发送验证码给用户,并将验证码set到redis中 //生成6位数随机验证码 auth_code := fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000)) //调用短信服务商,发送短信,此处代码暂时省略 fmt.Println(auth_code)
//将手机号及验证码set到redis中,过期时间为30s redis.RedisDB.Set(phone, auth_code, 30*time.Second) apicommon.ReturnSuccessResponse(ct "success", 0, nil) }}
复制代码


经过编译及运行以上代码后,进行测试和验证


# curl --location --request POST 'localhost:9100/api/v2/sso/getAuthCode' \--form 'telephone="11081719844"'
{ "message": "手机号错误!", "code": -1, "result": "Wrong phone number"}
复制代码


因为传入的手机号码错误,所以报错:手机号错误


# curl --location --request POST 'localhost:9100/api/v2/sso/getAuthCode' \--form 'telephone="18281981546"'
{ "message": "success", "code": 0, "result": "nil"}
复制代码


如果验证手机号码通过,则会返回成功的 message: success 和 code: 0 和 result: nil。


如果 API 是对外的,错误信息数量有限,则制定错误码非常容易,强烈建议使用错误码。如果是内部系统,特别是庞大的系统,内部错误会非常多,这时候没必要为每一个错误制定错误码,而只需为常见的错误制定错误码,对于普通的错误,系统在处理时会统一作为 InternalServerError 处理。

发布于: 刚刚阅读数: 2
用户头像

还未添加个人签名 2020.11.21 加入

还未添加个人简介

评论

发布
暂无评论
从零开发区块链应用(四)--自定义业务错误信息