您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Go應用中優雅處理Error的技巧有哪些,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
我們將重寫的Go里自帶的error類型,首先從一個自定義的錯誤類型開始,該錯誤類型將在程序中識別為error類型。因此,我們引入一個封裝了Go的 error的新自定義Error類型。
type GoError struct { error }
當我們在Go中說error是一個值時,它是字符串值 - 任何實現了Error() string函數的類型都可以視作error類型。將字符串值視為error會使跨層的error處理復雜化,因此處理error字符串信息并不是正確的方法。所以我們可以把字符串和錯誤碼解耦:
type GoError struct { error Code string }
現在對error的處理將基于錯誤碼Code字段而不是字符串。讓我們通過上下文數據進一步對錯誤字符串進行解耦,在上下文數據中可以使用i18n包進行國際化。
type GoError struct { error Code string Data map[string]interface{} }
Data包含用于構造錯誤字符串的上下文數據。錯誤字符串可以通過數據模板化:
//i18N def
"InvalidParamValue": "Invalid parameter value '{{.actual}}', expected '{{.expected}}' for '{{.name}}'"
在i18N定義文件中,錯誤碼Code將會映射到使用Data構建的模板化的錯誤字符串中。
error可能發生在任何一層,有必要為每一層提供處理error的選項,并在不丟失原始error值的情況下進一步使用附加的上下文信息對error進行包裝。GoError結構體可以用Causes進一步封裝,用來保存整個錯誤堆棧。
type GoError struct { error Code string Data map[string]interface{} Causes []error }
如果必須保存多個error數據,則causes是一個數組類型,并將其設置為基本error類型,以便在程序中包含該原因的第三方錯誤。
標記層組件將有助于識別error發生在哪一層,并且可以避免不必要的error wrap。例如,如果service類型的error組件發生在服務層,則可能不需要wrap error。檢查組件信息將有助于防止暴露給用戶不應該通知的error,比如數據庫error:
type GoError struct { error Code string Data map[string]interface{} Causes []error Component ErrComponent } type ErrComponent string const ( ErrService ErrComponent = "service" ErrRepo ErrComponent = "repository" ErrLib ErrComponent = "library" )
添加一個錯誤響應類型這樣可以支持error分類,以便于了解什么錯誤類型。例如,可以根據響應類型(如NotFound)對error進行分類,像DbRecordNotFound、ResourceNotFound、UserNotFound等等的error都可以歸類為 NotFound error。這在多層應用程序開發過程中非常有用,而且是可選的封裝:
type GoError struct { error Code string Data map[string]interface{} Causes []error Component ErrComponent ResponseType ResponseErrType } type ResponseErrType string const ( BadRequest ResponseErrType = "BadRequest" Forbidden ResponseErrType = "Forbidden" NotFound ResponseErrType = "NotFound" AlreadyExists ResponseErrType = "AlreadyExists" )
在少數情況下,出現error會進行重試。retry字段可以通過設置Retryable標記來決定是否要進行error重試:
type GoError struct { error Code string Message string Data map[string]interface{} Causes []error Component ErrComponent ResponseType ResponseErrType Retryable bool }
通過定義一個帶有GoError實現的顯式error接口,可以簡化error檢查:
package goerr type Error interface { error Code() string Message() string Cause() error Causes() []error Data() map[string]interface{} String() string ResponseErrType() ResponseErrType SetResponseType(r ResponseErrType) Error Component() ErrComponent SetComponent(c ErrComponent) Error Retryable() bool SetRetryable() Error }
有了上述的封裝方式,更重要的是對error進行抽象,將這些封裝保存在同一地方,并提供error函數的可重用性
func ResourceNotFound(id, kind string, cause error) GoError { data := map[string]interface{}{"kind": kind, "id": id} return GoError{ Code: "ResourceNotFound", Data: data, Causes: []error{cause}, Component: ErrService, ResponseType: NotFound, Retryable: false, } }
這個error函數抽象了ResourceNotFound這個error,開發者可以使用這個函數來返回error對象而不是每次創建一個新的對象:
//UserService user, err := u.repo.FindUser(ctx, userId) if err != nil { if err.ResponseType == NotFound { return ResourceNotFound(userUid, "User", err) } return err }
關于“Go應用中優雅處理Error的技巧有哪些”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。