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

一、如何自定义错误信息

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

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

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

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

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

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

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

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

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

1.2 错误码设计

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

1 00 01
服务级错误 服务模块 具体错误代码
  • 第一位是服务级别, 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 处理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,322评论 5 465
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,288评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,227评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,015评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,936评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,534评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,995评论 3 389
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,616评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,907评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,923评论 2 315
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,741评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,525评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,016评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,141评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,453评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,054评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,249评论 2 339

推荐阅读更多精彩内容