dongrang9300 2016-03-13 22:45
浏览 52
已采纳

在REST API上执行错误处理

I am very new to go and have deployed a small service with an API endpoint.

I have heard/read that go doesn't use try/catch so I am trying to figure out how I can "catch" any problems happening from my service call from my API and make sure that the resource server doesn't go down.

My code for my API looks like the following..

I have a routes.go file with the following

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

I have a 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);
}

and I have a bus.go which has the HandleCustomerLocationChange(...) function.

func HandleCustomerLocationChange(custLoc CustomerLocation) {

    endpoint := og.Getenv("RABBIT_ENDPOINT")
    conn, err := amqp.Dial("amqp://guest:guest@" + 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)

}

My question is how should I modify both the HandleCustomerLocationChange(...) function and if necessaryCustomerLocationChange(..)` handler to handle errors properly so that if an error occurs, my entire API doesn't go down?

  • 写回答

1条回答 默认 最新

  • duanri1985 2016-03-14 00:39
    关注

    Go suggests a different approach, that errors are not exceptional, they're normal events, just less common.

    Taking an example from the code above:

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

    Here, a panic (without recovery) terminates the process, shutting down the web server. Seems an overly severe response to not fully reading a request.

    What do you want to do? It may be appropriate to tell the client who made the request:

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

    You might want to return a json encoded response, or give a generic message to the client avoid exposing too much, and log the specific error details.

    For general functions it's idiomatic to return the error as the last return parameter. In the specific example you mentioned:

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

    Instead, check if the connection failed, and return the error to the caller. Handle it in the calling function, or add information and propagate it up the call stack.

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

    Propagating the error in this way gives a concise explanation of the root cause, like the 5 whys technique, eg:

    "did not update client location: did not connect to rabbitmq: network address 1.2.3 unreachable"

    Another convention is to deal with errors first and return early. This helps to reduce nesting.

    See also the many error handling resources, like error handling in a web application, Go by Example, Error Handling and Go, errors are values and Defer, Panic & Recover. The source code of the error package is interesting, as is Russ Cox's comment on error handling, and Nathan Youngman's To Err is Human.

    Also interesting is Upspin's concept of an operational trace, rather than a stack trace.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 2020长安杯与连接网探
  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么
  • ¥15 banner广告展示设置多少时间不怎么会消耗用户价值
  • ¥16 mybatis的代理对象无法通过@Autowired装填
  • ¥15 可见光定位matlab仿真
  • ¥15 arduino 四自由度机械臂
  • ¥15 wordpress 产品图片 GIF 没法显示
  • ¥15 求三国群英传pl国战时间的修改方法
  • ¥15 matlab代码代写,需写出详细代码,代价私
  • ¥15 ROS系统搭建请教(跨境电商用途)