在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ GO/ 11.1 錯(cuò)誤處理
7 文本處理
3 Web基礎(chǔ)
14 擴(kuò)展Web框架
10.4 小結(jié)
2.2 Go基礎(chǔ)
2.8 總結(jié)
6.1 session和cookie
5.5 使用beedb庫(kù)進(jìn)行ORM開(kāi)發(fā)
8.3 REST
13.6 小結(jié)
5.4 使用PostgreSQL數(shù)據(jù)庫(kù)
14.6 pprof支持
14.1 靜態(tài)文件支持
11.2 使用GDB調(diào)試
7.7 小結(jié)
1 GO環(huán)境配置
14.5 多語(yǔ)言支持
7.1 XML處理
1.5 總結(jié)
13 如何設(shè)計(jì)一個(gè)Web框架
14.3 表單及驗(yàn)證支持
12 部署與維護(hù)
10 國(guó)際化和本地化
1.1 Go 安裝
6.2 Go如何使用session
5.6 NOSQL數(shù)據(jù)庫(kù)操作
6.5 小結(jié)
9.4 避免SQL注入
12.1 應(yīng)用日志
4.2 驗(yàn)證表單的輸入
10.1 設(shè)置默認(rèn)地區(qū)
1.3 Go 命令
9.6 加密和解密數(shù)據(jù)
4.1 處理表單的輸入
4.4 防止多次遞交表單
11.3 Go怎么寫(xiě)測(cè)試用例
8 Web服務(wù)
12.3 應(yīng)用部署
5.7 小結(jié)
12.5 小結(jié)
11 錯(cuò)誤處理,調(diào)試和測(cè)試
9.2 確保輸入過(guò)濾
14.2 Session支持
6.4 預(yù)防session劫持
12.4 備份和恢復(fù)
8.1 Socket編程
13.1 項(xiàng)目規(guī)劃
13.4 日志和配置設(shè)計(jì)
7.6 字符串處理
13.2 自定義路由器設(shè)計(jì)
6.3 session存儲(chǔ)
3.4 Go的http包詳解
8.2 WebSocket
10.3 國(guó)際化站點(diǎn)
7.5 文件操作
7.4 模板處理
9.1 預(yù)防CSRF攻擊
13.3 controller設(shè)計(jì)
2.6 interface
14.4 用戶認(rèn)證
2.3 流程和函數(shù)
附錄A 參考資料
11.1 錯(cuò)誤處理
9.5 存儲(chǔ)密碼
9.3 避免XSS攻擊
12.2 網(wǎng)站錯(cuò)誤處理
6 session和數(shù)據(jù)存儲(chǔ)
2.4 struct類型
3.3 Go如何使得Web工作
2.5 面向?qū)ο?/span>
3.1 Web工作方式
1.2 GOPATH與工作空間
2.1 你好,Go
9.7 小結(jié)
13.5 實(shí)現(xiàn)博客的增刪改
7.2 JSON處理
10.2 本地化資源
7.3 正則處理
2 Go語(yǔ)言基礎(chǔ)
5.1 database/sql接口
4.5 處理文件上傳
8.5 小結(jié)
4.3 預(yù)防跨站腳本
5.3 使用SQLite數(shù)據(jù)庫(kù)
14.7 小結(jié)
3.2 Go搭建一個(gè)Web服務(wù)器
2.7 并發(fā)
5 訪問(wèn)數(shù)據(jù)庫(kù)
4 表單
3.5 小結(jié)
1.4 Go開(kāi)發(fā)工具
11.4 小結(jié)
9 安全與加密
5.2 使用MySQL數(shù)據(jù)庫(kù)
4.6 小結(jié)
8.4 RPC

11.1 錯(cuò)誤處理

Go語(yǔ)言主要的設(shè)計(jì)準(zhǔn)則是:簡(jiǎn)潔、明白,簡(jiǎn)潔是指語(yǔ)法和C類似,相當(dāng)?shù)暮?jiǎn)單,明白是指任何語(yǔ)句都是很明顯的,不含有任何隱含的東西,在錯(cuò)誤處理方案的設(shè)計(jì)中也貫徹了這一思想。我們知道在C語(yǔ)言里面是通過(guò)返回-1或者NULL之類的信息來(lái)表示錯(cuò)誤,但是對(duì)于使用者來(lái)說(shuō),不查看相應(yīng)的API說(shuō)明文檔,根本搞不清楚這個(gè)返回值究竟代表什么意思,比如:返回0是成功,還是失敗,而Go定義了一個(gè)叫做error的類型,來(lái)顯式表達(dá)錯(cuò)誤。在使用時(shí),通過(guò)把返回的error變量與nil的比較,來(lái)判定操作是否成功。例如os.Open函數(shù)在打開(kāi)文件失敗時(shí)將返回一個(gè)不為nil的error變量

func Open(name string) (file *File, err error)

下面這個(gè)例子通過(guò)調(diào)用os.Open打開(kāi)一個(gè)文件,如果出現(xiàn)錯(cuò)誤,那么就會(huì)調(diào)用log.Fatal來(lái)輸出錯(cuò)誤信息:

f, err := os.Open("filename.ext")
if err != nil {
    log.Fatal(err)
}

類似于os.Open函數(shù),標(biāo)準(zhǔn)包中所有可能出錯(cuò)的API都會(huì)返回一個(gè)error變量,以方便錯(cuò)誤處理,這個(gè)小節(jié)將詳細(xì)地介紹error類型的設(shè)計(jì),和討論開(kāi)發(fā)Web應(yīng)用中如何更好地處理error。

Error類型

error類型是一個(gè)接口類型,這是它的定義:

type error interface {
    Error() string
}

error是一個(gè)內(nèi)置的接口類型,我們可以在/builtin/包下面找到相應(yīng)的定義。而我們?cè)诤芏鄡?nèi)部包里面用到的 error是errors包下面的實(shí)現(xiàn)的私有結(jié)構(gòu)errorString

// errorString is a trivial implementation of error.
type errorString struct {
    s string
}

func (e *errorString) Error() string {
    return e.s
}

你可以通過(guò)errors.New把一個(gè)字符串轉(zhuǎn)化為errorString,以得到一個(gè)滿足接口error的對(duì)象,其內(nèi)部實(shí)現(xiàn)如下:

// New returns an error that formats as the given text.
func New(text string) error {
    return &errorString{text}
}

下面這個(gè)例子演示了如何使用errors.New:

func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    }
    // implementation
}

在下面的例子中,我們?cè)谡{(diào)用Sqrt的時(shí)候傳遞的一個(gè)負(fù)數(shù),然后就得到了non-nil的error對(duì)象,將此對(duì)象與nil比較,結(jié)果為true,所以fmt.Println(fmt包在處理error時(shí)會(huì)調(diào)用Error方法)被調(diào)用,以輸出錯(cuò)誤,請(qǐng)看下面調(diào)用的示例代碼:

f, err := Sqrt(-1)
if err != nil {
    fmt.Println(err)
}   

自定義Error

通過(guò)上面的介紹我們知道error是一個(gè)interface,所以在實(shí)現(xiàn)自己的包的時(shí)候,通過(guò)定義實(shí)現(xiàn)此接口的結(jié)構(gòu),我們就可以實(shí)現(xiàn)自己的錯(cuò)誤定義,請(qǐng)看來(lái)自Json包的示例:

type SyntaxError struct {
    msg    string // 錯(cuò)誤描述
    Offset int64  // 錯(cuò)誤發(fā)生的位置
}

func (e *SyntaxError) Error() string { return e.msg }

Offset字段在調(diào)用Error的時(shí)候不會(huì)被打印,但是我們可以通過(guò)類型斷言獲取錯(cuò)誤類型,然后可以打印相應(yīng)的錯(cuò)誤信息,請(qǐng)看下面的例子:

if err := dec.Decode(&val); err != nil {
    if serr, ok := err.(*json.SyntaxError); ok {
        line, col := findLine(f, serr.Offset)
        return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err)
    }
    return err
}

需要注意的是,函數(shù)返回自定義錯(cuò)誤時(shí),返回值推薦設(shè)置為error類型,而非自定義錯(cuò)誤類型,特別需要注意的是不應(yīng)預(yù)聲明自定義錯(cuò)誤類型的變量。例如:

func Decode() *SyntaxError { // 錯(cuò)誤,將可能導(dǎo)致上層調(diào)用者err!=nil的判斷永遠(yuǎn)為true。
    var err *SyntaxError     // 預(yù)聲明錯(cuò)誤變量
    if 出錯(cuò)條件 {
        err = &SyntaxError{}
    }
    return err               // 錯(cuò)誤,err永遠(yuǎn)等于非nil,導(dǎo)致上層調(diào)用者err!=nil的判斷始終為true
}

原因見(jiàn) http://golang.org/doc/faq#nil_error

上面例子簡(jiǎn)單的演示了如何自定義Error類型。但是如果我們還需要更復(fù)雜的錯(cuò)誤處理呢?此時(shí),我們來(lái)參考一下net包采用的方法:

package net

type Error interface {
    error
    Timeout() bool   // Is the error a timeout?
    Temporary() bool // Is the error temporary?
}

在調(diào)用的地方,通過(guò)類型斷言err是不是net.Error,來(lái)細(xì)化錯(cuò)誤的處理,例如下面的例子,如果一個(gè)網(wǎng)絡(luò)發(fā)生臨時(shí)性錯(cuò)誤,那么將會(huì)sleep 1秒之后重試:

if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
    time.Sleep(1e9)
    continue
}
if err != nil {
    log.Fatal(err)
}

錯(cuò)誤處理

Go在錯(cuò)誤處理上采用了與C類似的檢查返回值的方式,而不是其他多數(shù)主流語(yǔ)言采用的異常方式,這造成了代碼編寫(xiě)上的一個(gè)很大的缺點(diǎn):錯(cuò)誤處理代碼的冗余,對(duì)于這種情況是我們通過(guò)復(fù)用檢測(cè)函數(shù)來(lái)減少類似的代碼。

請(qǐng)看下面這個(gè)例子代碼:

func init() {
    http.HandleFunc("/view", viewRecord)
}

func viewRecord(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
    record := new(Record)
    if err := datastore.Get(c, key, record); err != nil {
        http.Error(w, err.Error(), 500)
        return
    }
    if err := viewTemplate.Execute(w, record); err != nil {
        http.Error(w, err.Error(), 500)
    }
}

上面的例子中獲取數(shù)據(jù)和模板展示調(diào)用時(shí)都有檢測(cè)錯(cuò)誤,當(dāng)有錯(cuò)誤發(fā)生時(shí),調(diào)用了統(tǒng)一的處理函數(shù)http.Error,返回給客戶端500錯(cuò)誤碼,并顯示相應(yīng)的錯(cuò)誤數(shù)據(jù)。但是當(dāng)越來(lái)越多的HandleFunc加入之后,這樣的錯(cuò)誤處理邏輯代碼就會(huì)越來(lái)越多,其實(shí)我們可以通過(guò)自定義路由器來(lái)縮減代碼(實(shí)現(xiàn)的思路可以參考第三章的HTTP詳解)。

type appHandler func(http.ResponseWriter, *http.Request) error

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if err := fn(w, r); err != nil {
        http.Error(w, err.Error(), 500)
    }
}

上面我們定義了自定義的路由器,然后我們可以通過(guò)如下方式來(lái)注冊(cè)函數(shù):

func init() {
    http.Handle("/view", appHandler(viewRecord))
}

當(dāng)請(qǐng)求/view的時(shí)候我們的邏輯處理可以變成如下代碼,和第一種實(shí)現(xiàn)方式相比較已經(jīng)簡(jiǎn)單了很多。

func viewRecord(w http.ResponseWriter, r *http.Request) error {
    c := appengine.NewContext(r)
    key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
    record := new(Record)
    if err := datastore.Get(c, key, record); err != nil {
        return err
    }
    return viewTemplate.Execute(w, record)
}

上面的例子錯(cuò)誤處理的時(shí)候所有的錯(cuò)誤返回給用戶的都是500錯(cuò)誤碼,然后打印出來(lái)相應(yīng)的錯(cuò)誤代碼,其實(shí)我們可以把這個(gè)錯(cuò)誤信息定義的更加友好,調(diào)試的時(shí)候也方便定位問(wèn)題,我們可以自定義返回的錯(cuò)誤類型:

type appError struct {
    Error   error
    Message string
    Code    int
}

這樣我們的自定義路由器可以改成如下方式:

type appHandler func(http.ResponseWriter, *http.Request) *appError

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if e := fn(w, r); e != nil { // e is *appError, not os.Error.
        c := appengine.NewContext(r)
        c.Errorf("%v", e.Error)
        http.Error(w, e.Message, e.Code)
    }
}

這樣修改完自定義錯(cuò)誤之后,我們的邏輯處理可以改成如下方式:

func viewRecord(w http.ResponseWriter, r *http.Request) *appError {
    c := appengine.NewContext(r)
    key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
    record := new(Record)
    if err := datastore.Get(c, key, record); err != nil {
        return &appError{err, "Record not found", 404}
    }
    if err := viewTemplate.Execute(w, record); err != nil {
        return &appError{err, "Can't display record", 500}
    }
    return nil
}

如上所示,在我們?cè)L問(wèn)view的時(shí)候可以根據(jù)不同的情況獲取不同的錯(cuò)誤碼和錯(cuò)誤信息,雖然這個(gè)和第一個(gè)版本的代碼量差不多,但是這個(gè)顯示的錯(cuò)誤更加明顯,提示的錯(cuò)誤信息更加友好,擴(kuò)展性也比第一個(gè)更好。

總結(jié)

在程序設(shè)計(jì)中,容錯(cuò)是相當(dāng)重要的一部分工作,在Go中它是通過(guò)錯(cuò)誤處理來(lái)實(shí)現(xiàn)的,error雖然只是一個(gè)接口,但是其變化卻可以有很多,我們可以根據(jù)自己的需求來(lái)實(shí)現(xiàn)不同的處理,最后介紹的錯(cuò)誤處理方案,希望能給大家在如何設(shè)計(jì)更好Web錯(cuò)誤處理方案上帶來(lái)一點(diǎn)思路。

links