使用自定义错误结构和结构验证实现错误处理?



我对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 表示内部服务器错误,我想使用上面的错误结构传递自定义消息。

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

不过对您的工作有几点评论:

  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)
}

最新更新