<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 11.1 錯誤處理 Go語言主要的設計準則是:簡潔、明白,簡潔是指語法和C類似,相當的簡單,明白是指任何語句都是很明顯的,不含有任何隱含的東西,在錯誤處理方案的設計中也貫徹了這一思想。我們知道在C語言里面是通過返回-1或者NULL之類的信息來表示錯誤,但是對于使用者來說,不查看相應的API說明文檔,根本搞不清楚這個返回值究竟代表什么意思,比如:返回0是成功,還是失敗,而Go定義了一個叫做error的類型,來顯式表達錯誤。在使用時,通過把返回的error變量與nil的比較,來判定操作是否成功。例如`os.Open`函數在打開文件失敗時將返回一個不為nil的error變量 func Open(name string) (file *File, err error) 下面這個例子通過調用`os.Open`打開一個文件,如果出現錯誤,那么就會調用`log.Fatal`來輸出錯誤信息: f, err := os.Open("filename.ext") if err != nil { log.Fatal(err) } 類似于`os.Open`函數,標準包中所有可能出錯的API都會返回一個error變量,以方便錯誤處理,這個小節將詳細地介紹error類型的設計,和討論開發Web應用中如何更好地處理error。 ## Error類型 error類型是一個接口類型,這是它的定義: type error interface { Error() string } error是一個內置的接口類型,我們可以在/builtin/包下面找到相應的定義。而我們在很多內部包里面用到的 error是errors包下面的實現的私有結構errorString // errorString is a trivial implementation of error. type errorString struct { s string } func (e *errorString) Error() string { return e.s } 你可以通過`errors.New`把一個字符串轉化為errorString,以得到一個滿足接口error的對象,其內部實現如下: // New returns an error that formats as the given text. func New(text string) error { return &errorString{text} } 下面這個例子演示了如何使用`errors.New`: func Sqrt(f float64) (float64, error) { if f < 0 { return 0, errors.New("math: square root of negative number") } // implementation } 在下面的例子中,我們在調用Sqrt的時候傳遞的一個負數,然后就得到了non-nil的error對象,將此對象與nil比較,結果為true,所以fmt.Println(fmt包在處理error時會調用Error方法)被調用,以輸出錯誤,請看下面調用的示例代碼: f, err := Sqrt(-1) if err != nil { fmt.Println(err) } ## 自定義Error 通過上面的介紹我們知道error是一個interface,所以在實現自己的包的時候,通過定義實現此接口的結構,我們就可以實現自己的錯誤定義,請看來自Json包的示例: type SyntaxError struct { msg string // 錯誤描述 Offset int64 // 錯誤發生的位置 } func (e *SyntaxError) Error() string { return e.msg } Offset字段在調用Error的時候不會被打印,但是我們可以通過類型斷言獲取錯誤類型,然后可以打印相應的錯誤信息,請看下面的例子: 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 } 需要注意的是,函數返回自定義錯誤時,返回值推薦設置為error類型,而非自定義錯誤類型,特別需要注意的是不應預聲明自定義錯誤類型的變量。例如: func Decode() *SyntaxError { // 錯誤,將可能導致上層調用者err!=nil的判斷永遠為true。 var err *SyntaxError // 預聲明錯誤變量 if 出錯條件 { err = &SyntaxError{} } return err // 錯誤,err永遠等于非nil,導致上層調用者err!=nil的判斷始終為true } 原因見 http://golang.org/doc/faq#nil_error 上面例子簡單的演示了如何自定義Error類型。但是如果我們還需要更復雜的錯誤處理呢?此時,我們來參考一下net包采用的方法: package net type Error interface { error Timeout() bool // Is the error a timeout? Temporary() bool // Is the error temporary? } 在調用的地方,通過類型斷言err是不是net.Error,來細化錯誤的處理,例如下面的例子,如果一個網絡發生臨時性錯誤,那么將會sleep 1秒之后重試: if nerr, ok := err.(net.Error); ok && nerr.Temporary() { time.Sleep(1e9) continue } if err != nil { log.Fatal(err) } ## 錯誤處理 Go在錯誤處理上采用了與C類似的檢查返回值的方式,而不是其他多數主流語言采用的異常方式,這造成了代碼編寫上的一個很大的缺點:錯誤處理代碼的冗余,對于這種情況是我們通過復用檢測函數來減少類似的代碼。 請看下面這個例子代碼: 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) } } 上面的例子中獲取數據和模板展示調用時都有檢測錯誤,當有錯誤發生時,調用了統一的處理函數`http.Error`,返回給客戶端500錯誤碼,并顯示相應的錯誤數據。但是當越來越多的HandleFunc加入之后,這樣的錯誤處理邏輯代碼就會越來越多,其實我們可以通過自定義路由器來縮減代碼(實現的思路可以參考第三章的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) } } 上面我們定義了自定義的路由器,然后我們可以通過如下方式來注冊函數: func init() { http.Handle("/view", appHandler(viewRecord)) } 當請求/view的時候我們的邏輯處理可以變成如下代碼,和第一種實現方式相比較已經簡單了很多。 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) } 上面的例子錯誤處理的時候所有的錯誤返回給用戶的都是500錯誤碼,然后打印出來相應的錯誤代碼,其實我們可以把這個錯誤信息定義的更加友好,調試的時候也方便定位問題,我們可以自定義返回的錯誤類型: 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) } } 這樣修改完自定義錯誤之后,我們的邏輯處理可以改成如下方式: 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 } 如上所示,在我們訪問view的時候可以根據不同的情況獲取不同的錯誤碼和錯誤信息,雖然這個和第一個版本的代碼量差不多,但是這個顯示的錯誤更加明顯,提示的錯誤信息更加友好,擴展性也比第一個更好。 ## 總結 在程序設計中,容錯是相當重要的一部分工作,在Go中它是通過錯誤處理來實現的,error雖然只是一個接口,但是其變化卻可以有很多,我們可以根據自己的需求來實現不同的處理,最后介紹的錯誤處理方案,希望能給大家在如何設計更好Web錯誤處理方案上帶來一點思路。 ## links * [目錄](<preface.md>) * 上一節: [錯誤處理,調試和測試](<11.0.md>) * 下一節: [使用GDB調試](<11.2.md>)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看