使用 Go 定義錯誤碼

簡介

不管在什麼系統中, 定義錯誤碼都是必不可少的.

錯誤碼可以幫助定義問題, 通常錯誤碼設計爲某種模式結構,
可以判斷出錯誤的級別, 錯誤的模塊和具體錯誤信息.

設計錯誤碼

一個良好結構的錯誤碼有助於簡化問題描述,
當前設計的錯誤碼共有五位, 結構如下:

1 00 01
服務級別 模塊 具體錯誤

第一位是服務級別, 1 爲系統錯誤, 2 爲普通錯誤.

第二三位是模塊, 模塊不是指 Go 中的模塊, 而是指代某個範圍, 比如數據庫錯誤, 認證錯誤.

第四五位是具體錯誤, 比如數據庫錯誤中的插入錯誤, 找不到數據等.

定義錯誤碼的時候不光有 Code 數字, 也會有對應的文本信息, 通常, 文本分爲兩類,
一類是給用戶看的, 另一類是用於 debug 的.

代碼實現

在 pkg 目錄下新建一個 errno 目錄, 並創建相應的模塊.

package errno

import "fmt"

// 定義錯誤碼
type Errno struct {
    Code    int
    Message string
}

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

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

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

上面定義了兩種數據結構, 每一種都實現了 Error() 方法, 也就是繼承了 error 接口.

Errno 定義了錯誤碼的結構, 會在另一個文件中統一定義所有的錯誤碼.
Err 定義了完整的錯誤的結構, 通常可以理解爲 Errno 和一個內部錯誤的結合.

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

New 函數從一個 Errno 和 error 中返回新的 Err, 這樣就包裝了內部錯誤.

另一個重要的方法是解碼錯誤, 獲取 Code 和 Message.

// 解碼錯誤, 獲取 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()
}

常見的錯誤碼

在設計完成 errno 模塊之後, 我們需要定義一些常見的錯誤碼.

package errno

/*
錯誤碼設計
第一位表示錯誤級別, 1 爲系統錯誤, 2 爲普通錯誤
第二三位表示服務模塊代碼
第四五位表示具體錯誤代碼
*/

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

    // 系統錯誤, 前綴爲 100
    InternalServerError = &Errno{Code: 10001, Message: "內部服務器錯誤"}
    ErrBind             = &Errno{Code: 10002, Message: "請求參數錯誤"}
    ErrTokenSign        = &Errno{Code: 10003, Message: "簽名 jwt 時發生錯誤"}
    ErrEncrypt          = &Errno{Code: 10004, Message: "加密用戶密碼時發生錯誤"}

    // 數據庫錯誤, 前綴爲 201
    ErrDatabase = &Errno{Code: 20100, Message: "數據庫錯誤"}
    ErrFill     = &Errno{Code: 20101, Message: "從數據庫填充 struct 時發生錯誤"}

    // 認證錯誤, 前綴是 202
    ErrValidation   = &Errno{Code: 20201, Message: "驗證失敗"}
    ErrTokenInvalid = &Errno{Code: 20202, Message: "jwt 是無效的"}

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

總結

錯誤碼是 API 服務中不可缺少的一環, 但錯誤碼的結構設計卻是千差萬別,
這個時候還是要多參考一些大廠的開放 API 設計, 找出共性, 去粗取精.

當前部分的代碼

作爲版本 v0.5.0

說明, 當前分支下定義的錯誤碼和上文中顯示的不一樣, 還是以文章爲主,
因爲寫文章也是一個思考的過程, 所以有時會對代碼做一點改動.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章