日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

cad致命错误如何处理_Golang 如何优雅地处理错误

發布時間:2024/9/27 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 cad致命错误如何处理_Golang 如何优雅地处理错误 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

- 后端早讀課翻譯計劃 第二篇-?

本文提供了一個優雅的處理 Golang 中錯誤的方法,解決了 Golang error 只有字符串信息的局限性,提供了上下文信息、錯誤類型判斷的功能。

盡管 go 具有一個簡單的錯誤模型,但是乍一看,事情并沒有那么容易。在本文中,提供了一個很好的處理錯誤的策略并克服您可能遇到的問題。

首先,我們將分析 go 中的錯誤是什么。


然后,我們再看錯誤創建和處理之間的流程,并分析有可能出現的漏洞。

Go 的錯誤類型

查看內建的錯誤類型,我們可以得到一些結論:

// The error built-in interface type is the conventional interface for// representing an error condition, with the nil value representing no error.type error interface { Error() string}

// 內置錯誤接口類型用于表示錯誤狀況的普通接口,其中 nil 值表示沒有錯誤。

我們看到,錯誤是一個簡單的 interface,實現了的 Error 方法,返回一個字符串。

這個定義告訴我們,創建一個錯誤只需要一個簡單的字符串就可以了,所以如果我創建下面的結構體:

type MyCustomError stringfunc (err MyCustomError) Error() string { return string(err)}

我就得到了一個最簡單的錯誤定義。

注意:這里只是舉一個例子。我們可以使用 Go 標準庫里面的 fmt 和 errors 來創建錯誤:

import ( "errors" "fmt")simpleError := errors.New("a simple error")simpleError2 := fmt.Errorf("an error from a %s string", "formatted")

一段簡單的信息能否優雅の處理錯誤嗎?讓我們在最后回答這個問題,看看我是怎么做的。

Error flow?錯誤處理

我們已經知道了什么是錯誤,下一步我們來看看一個錯誤的生命周期是怎樣的。

簡單起見,不要重復自己的錯誤處理邏輯,最好一個地方只處理一個邏輯。

通過下面這個例子,我們看看為什么這么說:

// bad example of handling and returning the error at the same timefunc someFunc() (Result, error) { result, err := repository.Find(id) if err != nil { log.Errof(err) return Result{}, err } return result, nil}

//?錯誤的示范:在一個地方處理(打印)并返回了錯誤

這段代碼有什么問題嗎?

我們處理兩次錯誤,第一次打印了他,第二次把它返回給調用者。

也許你的團隊同事使用了這個方法,當錯誤返回時,他會將錯誤日志再一次的打印出來。在系統里就出現了日志噩夢(多次打印同一個日志)

想想看我們的應用里有三層,數據層、交互層、Web 服務層:

// The repository uses an external depedency ormfunc getFromRepository(id int) (Result, error) { result := Result{ID: id} err := orm.entity(&result) if err != nil { return Result{}, err } return result, nil}

按照我之前提到的原則,這是一個正確的錯誤處理方式:把錯誤返回到最上層。然后他會被打印到日志里。將錯誤收集反饋在 Web 服務層,只在一個地方處理錯誤。

但是這段代碼有一個問題。不幸的是, Go 的內置錯誤沒有提供錯誤棧跟蹤。除此之外,這個錯誤是在外部依賴下生成的,我們需要知道項目中的哪段代碼對這個錯誤負責。

github.com/pkg/errors? ?拯救了這個問題。

我將上面的方法重寫,添加堆棧跟蹤,以及從數據層獲取失敗的信息,而且是在不改動原始的錯誤下:

import "github.com/pkg/errors"// The repository uses an external depedency ormfunc getFromRepository(id int) (Result, error) { result := Result{ID: id} err := orm.entity(&result) if err != nil { return Result{}, errors.Wrapf(err, "error getting the result with id %d", id); } return result, nil}// after the error wraping the result will be// err.Error() -> error getting the result with id 10: whatever it comes from the orm

這個方法做的事兒是:在 ORM 返回的錯誤外面包裝一層,在不影響原始錯誤的情況下,創建一個堆棧跟蹤(譯者注:wrap 的嵌套)。

讓我們看下其他層是如何處理這個錯誤的。交互層:

func getInteractor(idString string) (Result, error) { id, err := strconv.Atoi(idString) if err != nil { return Result{}, errors.Wrapf(err, "interactor converting id to int") } return repository.getFromRepository(id)}

最頂層的 Web 服務層:

r := mux.NewRouter()r.HandleFunc("/result/{id}", ResultHandler)func ResultHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) result, err := interactor.getInteractor(vars["id"]) if err != nil { handleError(w, err) } fmt.Fprintf(w, result)}func handleError(w http.ResponseWriter, err error) { w.WriteHeader(http.StatusIntervalServerError) log.Errorf(err) fmt.Fprintf(w, err.Error())}

正如你所見,我們只在頂層處理了錯誤。這樣就完美了嗎?并不是。如果你注意到,我們在錯誤的情況下都返回了 HTTP status 500. ?除此之外,我們總是記錄相同的錯誤,比如 “result not found” ,這樣只會增加我們的日志噪音。

My Solution 解決方案

我們在上一個主題中看到,只在頂層處理錯誤時,簡單的字符串錯誤信息不足以讓我們做錯誤處理的決策。

我們知道,我們在錯誤中創建一些信息,我們通常會加入一些信息比如錯誤是在哪里發生的,錯誤需要在哪里被處理。

讓我們為解決這個問題定義三個目標:

  • 提供錯誤棧

  • 打印錯誤(比如,?Web 服務層)

  • 在必要時提供錯誤的上下文信息(比如,提示電子郵件格式不正確)

首先,我們創建個錯誤類型:

package errorsconst( NoType = ErrorType(iota) BadRequest NotFound //add any type you want)type ErrorType uinttype customError struct { errorType ErrorType originalError error contextInfo map[string]string}// Error returns the mssage of a customErrorfunc (error customError) Error() string { return error.originalError.Error()}// New creates a new customErrorfunc (type ErrorType) New(msg string) error { return customError{errorType: type, originalError: errors.New(msg)}}// New creates a new customError with formatted messagefunc (type ErrorType) Newf(msg string, args ...interface{}) error { err := fmt.Errof(msg, args...) return customError{errorType: type, originalError: err}}// Wrap creates a new wrapped errorfunc (type ErrorType) Wrap(err error, msg string) error { return type.Wrapf(err, msg)}// Wrap creates a new wrapped error with formatted messagefunc (type ErrorType) Wrapf(err error, msg string, args ...interface{}) error { newErr := errors.Wrapf(err, msg, args..) return customError{errorType: errorType, originalError: newErr}}

我只定義了public ErrorType 以及錯誤類型,我們可以創建新的錯誤,并且可以將已有的錯誤進行包裝。

但是我們缺少兩件事。

  • 如何在不導出 customError 的情況下檢查錯誤類型?

  • 我們如何添加/獲取錯誤的上下文,甚至是一個已存在的來自外部依賴的錯誤?

讓我們使用 github.com/pkg/errors 提供的策略。首先包裝這些庫方法:

// New creates a no type errorfunc New(msg string) error { return customError{errorType: NoType, originalError: errors.New(msg)}}// Newf creates a no type error with formatted messagefunc Newf(msg string, args ...interface{}) error { return customError{errorType: NoType, originalError: errors.New(fmt.Sprintf(msg, args...))}}// Wrap wrans an error with a stringfunc Wrap(err error, msg string) error { return Wrapf(err, msg)}// Cause gives the original errorfunc Cause(err error) error { return errors.Cause(err)}// Wrapf wraps an error with format stringfunc Wrapf(err error, msg string, args ...interface{}) error { wrappedError := errors.Wrapf(err, msg, args...) if customErr, ok := err.(customError); ok { return customError{ errorType: customErr.errorType, originalError: wrappedError, contextInfo: customErr.contextInfo, } } return customError{errorType: NoType, originalError: wrappedError}}

添加一些方法來處理上下文和類型來解決已知或者未知錯誤(NoType)。

// AddErrorContext adds a context to an errorfunc AddErrorContext(err error, field, message string) error { context := errorContext{Field: field, Message: message} if customErr, ok := err.(customError); ok { return customError{errorType: customErr.errorType, originalError: customErr.originalError, contextInfo: context} } return customError{errorType: NoType, originalError: err, contextInfo: context}}// GetErrorContext returns the error contextfunc GetErrorContext(err error) map[string]string { emptyContext := errorContext{} if customErr, ok := err.(customError); ok || customErr.contextInfo != emptyContext { return map[string]string{"field": customErr.context.Field, "message": customErr.context.Message} } return nil}// GetType returns the error typefunc GetType(err error) ErrorType { if customErr, ok := err.(customError); ok { return customErr.errorType } return NoType}

回到我們的例子,我們將使用這個新的 error?方法

import "github.com/our_user/our_project/errors"// The repository uses an external depedency ormfunc getFromRepository(id int) (Result, error) { result := Result{ID: id} err := orm.entity(&result) if err != nil { msg := fmt.Sprintf("error getting the result with id %d", id) switch err { case orm.NoResult: err = errors.Wrapf(err, msg); default: err = errors.NotFound(err, msg); } return Result{}, err } return result, nil}// after the error wraping the result will be// err.Error() -> error getting the result with id 10: whatever it comes from the orm

現在的交互層:

func getInteractor(idString string) (Result, error) { id, err := strconv.Atoi(idString) if err != nil { err = errors.BadRequest.Wrapf(err, "interactor converting id to int") err = errors.AddContext(err, "id", "wrong id format, should be an integer) return Result{}, err } return repository.getFromRepository(id)}

最后的 Web 服務層:

r := mux.NewRouter()r.HandleFunc("/result/{id}", ResultHandler)func ResultHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) result, err := interactor.getInteractor(vars["id"]) if err != nil { handleError(w, err) } fmt.Fprintf(w, result)}func handleError(w http.ResponseWriter, err error) { var status int errorType := errors.GetType(err) switch errorType { case BadRequest: status = http.StatusBadRequest case NotFound: status = http.StatusNotFound default: status = http.StatusInternalServerError } w.WriteHeader(status) if errorType == errors.NoType { log.Errorf(err) } fmt.Fprintf(w,"error %s", err.Error()) errorContext := errors.GetContext(err) if errorContext != nil { fmt.Printf(w, "context %v", errorContext) }}

如你所見,通過導出類型和一些導出的值,我們可以讓處理錯誤的生活更容易一點。在這個解決方案里的設計中,有一點我非常喜歡,就是在創建錯誤的時候我們明確了錯誤的具體類型。

github repository: https://github.com/henrmota/errors-handling-example

總結

以上是生活随笔為你收集整理的cad致命错误如何处理_Golang 如何优雅地处理错误的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。