2016-03-13 76 views
3

我很新,並且已經部署了一個帶有API端點的小型服務。在REST API上執行錯誤處理

我聽說/讀了不使用try/catch所以我想弄清楚如何從我的API服務調用中發現任何問題,並確保資源服務器不會去下。

我爲我的API代碼如下所示..

我有一個routes.go文件具有以下

package main 

import (
    "net/http" 

    "github.com/gorilla/mux" 
) 

type Route struct { 
    Name  string 
    Method  string 
    Pattern  string 
    HandlerFunc http.HandlerFunc 
} 

type Routes []Route 

func NewRouter() *mux.Router { 
    router := mux.NewRouter().StrictSlash(true) 
    for _, route := range routes { 
     router. 
      Methods(route.Method). 
      Path(route.Pattern). 
      Name(route.Name). 
      Handler(route.HandlerFunc) 
    } 

    return router 
} 

var routes = Routes{ 
    Route{ 
     "CustomerLocationCreate", 
     "POST", 
     "/tracking/customer", 
     CustomerLocationCreate, 
    }, 
} 

我有一個handlers.go

package main 

import (
    "encoding/json" 
    "net/http" 
    "io" 
    "io/ioutil" 
) 

//curl -H "Content-Type: application/json" -d '{"userId":"1234"}' http://localhost:8181/tracking/customer 
func CustomerLocationCreate(w http.ResponseWriter, r *http.Request) { 
    var location CustomerLocation 
    body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576)) 
    if err != nil { 
     panic(err) 
    } 
    if err := r.Body.Close(); err != nil { 
     panic(err) 
    } 
    if err := json.Unmarshal(body, &location); err != nil { 
     w.Header().Set("Content-Type", "application/json; charset=UTF-8") 
     w.WriteHeader(422) // unprocessable entity 
     if err := json.NewEncoder(w).Encode(err); err != nil { 
      panic(err) 
     } 
    } 

    c := RepoCreateCustomerLocation(location) 
    w.Header().Set("Content-Type", "application/json; charset=UTF-8") 
    w.WriteHeader(http.StatusCreated) 
    if err := json.NewEncoder(w).Encode(c); err != nil { 
     panic(err) 
    } 

    HandleCustomerLocationChange(c); 
} 

,我有一個bus.go其中有HandleCustomerLocationChange(...)功能。

func HandleCustomerLocationChange(custLoc CustomerLocation) { 

    endpoint := og.Getenv("RABBIT_ENDPOINT") 
    conn, err := amqp.Dial("amqp://guest:[email protected]" + endpoint) 
    failOnError(err, "Failed to connect to RabbitMQ") 
    defer conn.Close() 

    ch, err := conn.Channel() 
    failOnError(err, "Failed to open a channel") 
    defer ch.Close() 

    topic := "locationChange" 
    err = ch.ExchangeDeclare(
     topic, // name 
     "topic", // type 
     true, // durable 
     false, // auto-deleted 
     false, // internal 
     false, // no-wait 
     nil,  // arguments 
    ) 
    failOnError(err, "Failed to declare an exchange") 

    // Create JSON from the instance data. 
    body, _ := json.Marshal(custLoc) 
    // Convert bytes to string. 

    err = ch.Publish(
     topic, // exchange 
     "", // routing key 
     false, // mandatory 
     false, // immediate 
     amqp.Publishing{ 
      ContentType: "text/plain", 
      Body:  body, 
     }) 
    failOnError(err, "Failed to publish a message") 

    log.Printf(" [x] Sent %s", body) 

} 

我的問題是如何同時修改HandleCustomerLocationChange(...) function and if necessary CustomerLocationChange(..)`處理程序妥善處理錯誤,這樣如果發生錯誤,我的整個API不下去?

+3

不要叫慌。使用'http.Error'來編寫錯誤響應,或者編寫自己的錯誤響應來生成JSON錯誤響應。 – elithrar

回答

16

Go提出了一種不同的方法,即錯誤並不例外,它們是正常事件,不太常見。

以從上面的代碼的示例:

body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576)) 
if err != nil { 
    panic(err) 
} 

這裏,恐慌(沒有恢復)結束處理,關閉Web服務器。似乎對沒有完全閱讀請求的反應過於嚴厲。

你想做什麼?這可能是合適的分辨出誰發出請求的客戶端:

body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576)) 
if err != nil { 
    http.Error(w, err.Error(), http.StatusBadRequest) 
    return 
} 

您可能要返回一個JSON編碼的響應,或到客戶端避免暴露過多給一個通用的消息,並記錄具體錯誤的詳細信息。

對於一般函數來說,將錯誤作爲最後的返回參數返回是慣用的。在您提到的具體示例中:

func HandleCustomerLocationChange(custLoc CustomerLocation) 
... 
    conn, err := amqp.Dial(...) 
    failOnError(err, "Failed to connect to RabbitMQ") 

而是檢查連接是否失敗,並將錯誤返回給調用者。在調用函數中處理它,或者添加信息並將其傳播到調用棧中。

func HandleCustomerLocationChange(custLoc CustomerLocation) error 
... 
    conn, err := amqp.Dial(...) 
    if err != nil { 
     return fmt.Errorf("failed to connect to RabbitMQ: %s", err) 
    } 

傳播這樣的錯誤給出的根本原因的簡要說明,如5 whys technique,如:

「沒有更新客戶端的位置:沒有連接到RabbitMQ的:網絡地址1.2.3無法達到「

另一個約定是先處理錯誤並提前返回。這有助於減少嵌套。

另請參閱許多錯誤處理資源,如error handling in a web applicationGo by ExampleError Handling and Goerrors are valuesDefer, Panic & Recovererror package的源代碼很有趣,Russ Cox's comment on error handlingNathan Youngman's To Err is Human也是如此。

也有趣的是Upspin's concept of an operational trace,而不是一個堆棧跟蹤。