【问题标题】:Implementing Error handling with a custom error struct and struct validation?使用自定义错误结构和结构验证实现错误处理?
【发布时间】:2020-01-05 13:08:21
【问题描述】:

我对 Golang 非常陌生,我编写了一个小的 REST API 来熟悉该语言,并且我使用 Mux 作为路由器。我想知道是否可以在如何为我的小 API 实现错误处理方面获得一些帮助,但我在解决这个问题时遇到了麻烦。我想使用错误结构来处理消息。 以下是我的代码:

type Trade struct {
    ClientTradeId string `json:"clientTradeId"`
    Date          int    `json:"date"`
    Quantity      string `json:"quantity"`
    Price         string `json:"price"`
    Ticker        string `json:"ticker"`
}
type InternalTrade struct {
    Id string `json:"Id"`
    Trade *Trade `json:"Trade"`
}

type TradeSubmitted struct {

    TradeId string `json:"TradeId"`
    ClientTradeId string `json:"clientTradeId"`

}

type Error struct {
    Message string `json:"Message"`
}

var trades []InternalTrade

func getTrades(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")

    err := json.NewEncoder(w).Encode(trades)
    if err != nil {
        http.Error(w, err.Error(),500)
    }

}

func getTradeById(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    params := mux.Vars(r)
    for _, trade := range trades {
        if trade.Id == params["trade_id"] {
            json.NewEncoder(w).Encode(trade)
            return
        }
    }
    json.NewEncoder(w).Encode(&InternalTrade{})


}

func createTrade(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")

    var trade Trade
    var tradeSubmitted TradeSubmitted

    _ = json.NewDecoder(r.Body).Decode(&trade)

    internal := InternalTrade{
        Id: strconv.Itoa(rand.Intn(1000000)),
        Trade: &trade,
    }


    tradeSubmitted.ClientTradeId = trade.ClientTradeId
    tradeSubmitted.TradeId = internal.Id

    trades = append(trades, internal)

    json.NewEncoder(w).Encode(&tradeSubmitted)

}

func deleteTrade(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    params := mux.Vars(r)
    for idx, trade := range trades {
        if trade.Id == params["trade_id"] {
            trades = append(trades[:idx], trades[idx+1:]...)
            break
        }
    }
    json.NewEncoder(w).Encode(trades)

}

func updateTrade(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    params := mux.Vars(r)
    for idx, trade := range trades {
        if trade.Id == params["trade_id"] {
            trades = append(trades[:idx], trades[idx+1:]...)
            var internal InternalTrade
            _  = json.NewDecoder(r.Body).Decode(&internal.Trade)
            internal.Id = params["trade_id"]
            trades = append(trades, internal)
            json.NewEncoder(w).Encode(internal)
            return
        }
    }

}

func main() {
    router := mux.NewRouter()
    trades = append(trades, InternalTrade{ Id:"1", Trade: &Trade{ClientTradeId: "T-50264430-bc41", Date:20200101,
        Quantity:"100", Price:"10.00", Ticker:"APPL"}})
    trades = append(trades, InternalTrade{ Id:"2", Trade: &Trade{ClientTradeId: "T-99999999-ab14", Date:20200101,
        Quantity:"100", Price:"420.00", Ticker:"TSLA"}})


    router.HandleFunc("/v1/trades", getTrades).Methods("GET")
    router.HandleFunc("/v1/trades/{trade_id}", getTradeById).Methods("GET")
    router.HandleFunc("/v1/trades", createTrade).Methods("POST")
    router.HandleFunc("/v1/trades/{trade_id}", deleteTrade).Methods("DELETE")
    router.HandleFunc("/v1/trades/{trade_id}", updateTrade).Methods("PUT")
    log.Fatal(http.ListenAndServe(":8080", router))
}

我想帮助在我的 createTrade 函数上实现一些错误处理,以便在传递不正确的类型时捕获 400,当请求缺少字段时捕获 422。 500 表示内部服务器错误,我想使用上面的错误结构传递自定义消息。

【问题讨论】:

    标签: rest go


    【解决方案1】:

    从您的问题中不清楚您到底在寻找什么。请描述期望的行为以获得具体建议。

    虽然你的工作很少有 cmets:

    1. 代码大多是干净和惯用的。干得好!
    2. 总是!!!检查返回的错误并根据需要进行处理。 GO 不依赖 try/catch 块,错过的错误会使您的应用处于不正确的状态,并且没有明确报告。
    3. 使用fmt.Errorf - https://blog.golang.org/go1.13-errors 为错误添加额外的上下文
    4. http.Error 以纯文本形式返回错误。如果您以与成功响应相同的格式 (JSON) 返回它,您可以让客户的生活更轻松
    5. GO 在单独的 Goroutine 中执行每个请求(ServeHTTP 函数)。并发代码执行不应改变共享状态。您的 trades 切片由多个处理程序共享和编辑。这仅适用于测试项目,不应用于生产。

    这是在处理程序中处理错误的示例代码:

    func returnError(w http.ResponseWriter, err error, code int) {
        var errorData Error
        output, err := json.Marshal(&errorData)
        if err != nil {
            w.Write([]byte("Cannot serialize error"))
            return
        }
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(code)
        w.Write(output)
    }
    
    func parseRequest(r *http.Request) (Trade, error) {
        return Trade{}, nil
    }
    
    func saveTrade(t Trade) (TradeSubmitted, error) {
        return TradeSubmitted{}, nil
    }
    
    func createTrade(w http.ResponseWriter, r *http.Request) {
    
        trade, err := parseRequest(r)
        if err != nil {
            returnError(w, err, 400)
            return
        }
    
        tradeSubmitted, err := saveTrade(trade)
        if err != nil {
            returnError(w, err, 500)
            return
        }
    
        output, err := json.Marshal(&tradeSubmitted)
        if err != nil {
            err = fmt.Errorf("Cannot serialize Trade with id: xxx: %w", err)
            returnError(w, err, 500)
            return
        }
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusOK)
        w.Write(output)
    }
    

    【讨论】:

    • 嗨,德米特里,谢谢!我将在这里澄清并编辑我的帖子。我希望有人可以帮助我在上面的 api 中实现错误处理,特别是在我的 createTrade 函数中。我想处理错误:400、422、500。如何捕获这些错误并使用我的错误结构和状态码返回它们。
    • 我不会再对你的代码 sn-p 使用 mux 了吗?
    • 我更新了代码。您继续使用 mux 进行路由。我的代码只涵盖一个功能 - 单一路线
    猜你喜欢
    • 1970-01-01
    • 2013-09-25
    • 2022-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-14
    • 2016-08-18
    相关资源
    最近更新 更多