05使用 Go 定義錯(cuò)誤碼

簡(jiǎn)介

不管在什么系統(tǒng)中, 定義錯(cuò)誤碼都是必不可少的.

錯(cuò)誤碼可以幫助定義問題, 通常錯(cuò)誤碼設(shè)計(jì)為某種模式結(jié)構(gòu),
可以判斷出錯(cuò)誤的級(jí)別, 錯(cuò)誤的模塊和具體錯(cuò)誤信息.

設(shè)計(jì)錯(cuò)誤碼

一個(gè)良好結(jié)構(gòu)的錯(cuò)誤碼有助于簡(jiǎn)化問題描述,
當(dāng)前設(shè)計(jì)的錯(cuò)誤碼共有五位, 結(jié)構(gòu)如下:

1 00 01
服務(wù)級(jí)別 模塊 具體錯(cuò)誤

第一位是服務(wù)級(jí)別, 1 為系統(tǒng)錯(cuò)誤, 2 為普通錯(cuò)誤.

第二三位是模塊, 模塊不是指 Go 中的模塊, 而是指代某個(gè)范圍, 比如數(shù)據(jù)庫(kù)錯(cuò)誤, 認(rèn)證錯(cuò)誤.

第四五位是具體錯(cuò)誤, 比如數(shù)據(jù)庫(kù)錯(cuò)誤中的插入錯(cuò)誤, 找不到數(shù)據(jù)等.

定義錯(cuò)誤碼的時(shí)候不光有 Code 數(shù)字, 也會(huì)有對(duì)應(yīng)的文本信息, 通常, 文本分為兩類,
一類是給用戶看的, 另一類是用于 debug 的.

代碼實(shí)現(xiàn)

在 pkg 目錄下新建一個(gè) errno 目錄, 并創(chuàng)建相應(yīng)的模塊.

package errno

import "fmt"

// 定義錯(cuò)誤碼
type Errno struct {
    Code    int
    Message string
}

func (err Errno) Error() string {
    return err.Message
}

// 定義錯(cuò)誤
type Err struct {
    Code    int    // 錯(cuò)誤碼
    Message string // 展示給用戶看的
    Errord  error  // 保存內(nèi)部錯(cuò)誤信息
}

func (err *Err) Error() string {
    return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, err.Message, err.Errord)
}

上面定義了兩種數(shù)據(jù)結(jié)構(gòu), 每一種都實(shí)現(xiàn)了 Error() 方法, 也就是繼承了 error 接口.

Errno 定義了錯(cuò)誤碼的結(jié)構(gòu), 會(huì)在另一個(gè)文件中統(tǒng)一定義所有的錯(cuò)誤碼.
Err 定義了完整的錯(cuò)誤的結(jié)構(gòu), 通??梢岳斫鉃?Errno 和一個(gè)內(nèi)部錯(cuò)誤的結(jié)合.

// 使用 錯(cuò)誤碼 和 error 創(chuàng)建新的 錯(cuò)誤
func New(errno *Errno, err error) *Err {
    return &Err{
        Code:    errno.Code,
        Message: errno.Message,
        Errord:  err,
    }
}

New 函數(shù)從一個(gè) Errno 和 error 中返回新的 Err, 這樣就包裝了內(nèi)部錯(cuò)誤.

另一個(gè)重要的方法是解碼錯(cuò)誤, 獲取 Code 和 Message.

// 解碼錯(cuò)誤, 獲取 Code 和 Message
func DecodeErr(err error) (int, string) {
    if err == nil {
        return OK.Code, OK.Message
    }
    switch typed := err.(type) {
    case *Err:
        if typed.Code == ErrBind.Code {
            typed.Message = typed.Message + " 具體是 " + typed.Errord.Error()
        }
        return typed.Code, typed.Message
    case *Errno:
        return typed.Code, typed.Message
    default:
    }

    return InternalServerError.Code, err.Error()
}

常見的錯(cuò)誤碼

在設(shè)計(jì)完成 errno 模塊之后, 我們需要定義一些常見的錯(cuò)誤碼.

package errno

/*
錯(cuò)誤碼設(shè)計(jì)
第一位表示錯(cuò)誤級(jí)別, 1 為系統(tǒng)錯(cuò)誤, 2 為普通錯(cuò)誤
第二三位表示服務(wù)模塊代碼
第四五位表示具體錯(cuò)誤代碼
*/

var (
    OK = &Errno{Code: 0, Message: "OK"}

    // 系統(tǒng)錯(cuò)誤, 前綴為 100
    InternalServerError = &Errno{Code: 10001, Message: "內(nèi)部服務(wù)器錯(cuò)誤"}
    ErrBind             = &Errno{Code: 10002, Message: "請(qǐng)求參數(shù)錯(cuò)誤"}
    ErrTokenSign        = &Errno{Code: 10003, Message: "簽名 jwt 時(shí)發(fā)生錯(cuò)誤"}
    ErrEncrypt          = &Errno{Code: 10004, Message: "加密用戶密碼時(shí)發(fā)生錯(cuò)誤"}

    // 數(shù)據(jù)庫(kù)錯(cuò)誤, 前綴為 201
    ErrDatabase = &Errno{Code: 20100, Message: "數(shù)據(jù)庫(kù)錯(cuò)誤"}
    ErrFill     = &Errno{Code: 20101, Message: "從數(shù)據(jù)庫(kù)填充 struct 時(shí)發(fā)生錯(cuò)誤"}

    // 認(rèn)證錯(cuò)誤, 前綴是 202
    ErrValidation   = &Errno{Code: 20201, Message: "驗(yàn)證失敗"}
    ErrTokenInvalid = &Errno{Code: 20202, Message: "jwt 是無(wú)效的"}

    // 用戶錯(cuò)誤, 前綴為 203
    ErrUserNotFound      = &Errno{Code: 20301, Message: "用戶沒找到"}
    ErrPasswordIncorrect = &Errno{Code: 20302, Message: "密碼錯(cuò)誤"}
)

總結(jié)

錯(cuò)誤碼是 API 服務(wù)中不可缺少的一環(huán), 但錯(cuò)誤碼的結(jié)構(gòu)設(shè)計(jì)卻是千差萬(wàn)別,
這個(gè)時(shí)候還是要多參考一些大廠的開放 API 設(shè)計(jì), 找出共性, 去粗取精.

當(dāng)前部分的代碼

作為版本 v0.5.0

說(shuō)明, 當(dāng)前分支下定義的錯(cuò)誤碼和上文中顯示的不一樣, 還是以文章為主,
因?yàn)閷懳恼乱彩且粋€(gè)思考的過(guò)程, 所以有時(shí)會(huì)對(duì)代碼做一點(diǎn)改動(dòng).

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容