【问题标题】:How to determine what kind of error message I should return from golang API?如何确定我应该从 golang API 返回什么样的错误消息?
【发布时间】:2018-09-26 04:23:55
【问题描述】:

我有一个带有 SPA 的 GoLang API 来使用它。我对 API 中的错误所做的就是返回它们,直到我测试是否存在来自先前函数的错误的处理程序。如果有错误,我将它放在响应正文中,将状态码设置为 400 或 500 然后返回响应

在处理函数中,为了能够创建一个清晰的消息给客户端,我需要知道返回了什么样的错误,我该怎么做?

我知道错误类型,但我读到了 Dave Cheney 的建议,即只返回一个错误和一条消息(或换一种说法)。

但是如果API调用中可能会出现这么多种错误,那么是不是说在返回响应之前,我需要检查它们才知道应该返回什么消息?

【问题讨论】:

  • 如果是http错误,则使用http.Error返回相同的错误,如果是编译错误,则使用自定义http状态返回

标签: rest go


【解决方案1】:

关于错误首先要说的是,只是因为有一个错误界面

type error interface {
    Error() string
}

并不意味着从任何给定方法返回的error 只能附加该方法/信息。

一种常用的方法是定义自己的错误接口:

type myError interface {
    error // embeds the standard error interface
    OtherMethod() string // can define own methods here
}

在编写方法和函数时,非常重要返回 error 而不是 myError,否则您将该方法与错误实现结合起来,并在以后为您自己造成依赖噩梦。

现在我们已经决定可以从错误中返回额外信息,使用我们自己的错误接口,您有 3 个主要选择。

  1. 前哨错误
  2. 错误失败类型
  3. 行为错误

前哨错误

哨兵错误是定义为包级别变量的错误值,被导出并允许比较以检查错误。

package myPackage

var ErrConnectionFailed = errors.New("connection failed")

func Connect() error {
    // trimmed ...
    return ErrConnectionFailed
}

这个例子的消费者可以使用连接函数:

if err := myPackage.Connect(); err == myPackage.ErrConnectionFailed {
    // handle connection failed state
}

您可以做一个比较来检查返回的错误是否等于包的哨兵错误。缺点是使用errors.New("connection failed") 创建的任何错误都是相等的,并且不仅仅是来自myPackage 的错误。

错误失败类型

略好于哨兵错误的是错误失败类型。 我们已经看到您可以定义自己的错误接口,如果我们说现在是:

type MyError interface {
    error
    Failure() string
} 

type Err struct {
    failure string
}

func (e *Err) Error() string {
    // implement standard error
}

func (e *Err) Failure() string {
    return e.failure
}

const ConnFailed = "connection failed"

err := &Err{failure: ConnFailed}

在消费者代码中你会得到一个错误,检查它是否实现了MyError,然后用它来处理。

err := myPackage.Connect()

if myErr, ok := err.(myPackage.MyError); ok {
    // here you know err is a MyError
    if myErr.Failure() == myPackage.ConnFailed {
        // handle connection failed, could also use a switch instead of if
    }
}

现在您知道是什么导致了错误,这很好。但是你真的在乎是什么原因吗?或者你真的只关心你想要做什么来处理那个错误。

这是行为错误很好的地方。

行为错误

这类似于定义您自己的错误类型,但您可以定义报告有关该错误的信息的方法。鉴于上面的示例,您真的关心连接失败,还是只关心是否可以重试或是否需要再次错误调用堆栈?

package myPackage

// this interface could report if the error
// is temporary and if you could retry it
type tempErr interface {
    Temporary() bool
}

func (e *Err) Temporary() bool {
    // return if the error is temporary or not
}

现在在消费者中(注意你不需要使用myPackage.tempErr),如果错误是暂时的,你可以使用类型断言进行测试并处理重试情况:

err := myPackage.Connect()

if tmp, ok := err.(interface { Temporary() bool }); ok && tmp.Temporary() {
    // the error is temporary and you can retry the connection
}

要回答这个问题,如果没有您尝试实施的服务的具体细节,就很难说。但作为广泛的建议,我会尽量使用这 3 个示例中的最后一个。

如果您的服务使用者向您发送了一些无效的输入:

err := doThing(...)

if inv, ok := err.(interface { Invalid() bool }); ok && inv.Invalid() {
    // input is invalid, return 400 bad request status code etc.
}

如果您想将特定消息返回给消费者,您可以将其设为您的错误类型的方法。警告:这将使您的包知道它们正在用于 Web 服务等。

err := doThing(...)

if msg, ok := err.(interface { ResponseMsg() string }); ok {
    // write the message to the http response
    io.WriteString(response, msg.ResponseMsg())
}

TL;DR您需要处理所有情况,但您可以创建错误类型,使代码更易于使用!

【讨论】:

  • 缺少的一件事是如何确定函数返回的错误类型/类型。反射?
  • 我不确定尚未涵盖的用例是什么。根据您使用的代码返回的错误类型,有不同的检查方式。例如,io 包返回经过相等性测试的哨兵错误,os 包返回os.PathError,经过类型断言测试
  • 但是你怎么知道函数返回什么类型的错误呢?查看源代码?
  • 文档或源代码,没有很好的方式来实现这一点
猜你喜欢
  • 2015-10-31
  • 1970-01-01
  • 1970-01-01
  • 2016-04-15
  • 2020-09-07
  • 1970-01-01
  • 2018-05-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多