为了账号安全,请及时绑定邮箱和手机立即绑定

将外部错误映射到 golang 中的域错误

将外部错误映射到 golang 中的域错误

Go
Qyouu 2023-04-17 16:03:08
我有一个称为ComputeService实现特定域逻辑的服务类型。服务本身取决于调用的接口的实现,Computer该接口具有方法Computer.Compute(args...) (value, error)。如图所示,Compute它本身可能会返回某些错误。ComputeService需要使用正确的域错误代码从一组域错误中发送适当的错误,以便可以完成翻译,并且客户端也可以适当地处理错误。我的问题是,Computer实现应该将它们的失败包装在域错误中还是应该ComputeService这样做。如果ComputeService是这样做的人,那么它将必须知道Computer接口的不同实现返回的不同错误,在我看来这打破了抽象。两种方式如下所示:package arithmetictype Computer struct {}func (ac Computer) Compute(args ....) (value, error) {     // errors is a domain-errors package defined in compute service project     return errors.NewDivideByZero()}或者package computetype Service struct {}func (svc Service) Process(args...) error {    computer := findComputerImplementation(args...)    val, err := computer.Compute(args...)    if err != nil {       if err == arith.ErrDivideByZero {          // converting an arithmetic computer implementation           // specific error to domain error          return errors.NewDivideByZero()       } else if err == algebra.ErrInvalidCoEfficient {          // converting an algebraic computer implementation           // specific error to domain error          return errors.NewBadInput()       }       // some new implementation was used and we have no idea       // what errors it could be returning. so we have to send       // a internal server error equivalent here       return errors.NewInternalError()    }}
查看完整描述

2 回答

?
千巷猫影

TA贡献1829条经验 获得超7个赞

的实现者Computer应该用域错误来响应,因为它们是最接近操作的错误并且最能确定错误是什么。就像你说的,拥有这种逻辑打破 ComputeService了抽象。如果您需要将代码从特定Computer错误映射到域错误,请创建将主要逻辑与该错误包装代码分开的包装器结构。


要保留内部错误上下文,只需将原始错误嵌入到域错误中并创建IsSpecificDomainError帮助程序。


type MyDomainError struct {

    Err error

}


func NewMyDomainErr(err error) error {

    return &MyDomainError{err}

}


func IsMyDomainError(e error) bool {

    _, ok := err.(*MyDomainError)

    return ok

}


查看完整回答
反对 回复 2023-04-17
?
波斯汪

TA贡献1811条经验 获得超4个赞

要保留内部错误上下文,只需将原始错误嵌入到域错误中

我想我们都同意这strings.Contains(err.Error(), "not found")是脆弱的代码。

我希望我们也同意我们更愿意看到像errors.Is(err, os.ErrNotExist).

但要点是,在许多情况下,包的未来发展必须避免调用者依赖特定的错误结果来满足errors.Is(err, os.ErrNotExist),即使这是导致今天结果的根本原因。
这就像查看未导出的字段或比较错误文本 - 这是一个可能会改变的细节。

虽然strings.Contains看起来很脆弱,但errors.Is看起来并不脆弱,也不应被视为脆弱。
如果我们要避免它变得脆弱,那么我们需要为包提供一种方法来报告详细信息,而无需让客户对其进行测试。这种方式是无法解包的错误。

err.As():

var pe *os.PathError

if errors.As(err, &pe) {

     use(pe)

}

%在


func inner() error { return errors.New("inner error") }

func outer() error { return fmt.Errorf("outer error: %w", inner()) }


fmt.Fprintf("%+v", outer())

// outer error:

//     /path/to/file.go:123

//   - inner error:

//     /path/to/file.go:122

Go 1.13 的当前状态:

只是说明我认为团队提供的折衷解决方案:

  • fmt.Errorf目前被广泛用于包装错误并返回一个新的(不透明的)错误(因为您无法访问底层错误)。
    %w' 现在可用于显式选择返回可解包的错误。

  • errors 被设计为没有依赖关系的基础包,因此每个包都可以依赖它。

  • 团队同意在存在广泛分歧的领域下注,并希望发布足够多的内容(errors.Is,errors.As,对大多数人包装错误的方式的扩展),以便人们可以实现目标。

  • 泛型还没有出现,我们不知道它什么时候会出现:关于泛型的激烈讨论将使这个关于“错误 2 值”的问题看起来像儿戏。
    errors.Is并且errors.As干净简洁,足以长时间舒适。

大多数有争议的事情都被推迟到 1.14。

  • Wrapf不能存在错误,因为它是一个基础包。

  • Wrapf意味着团队必须决定在nil传递错误时会发生什么:下注。

  • Wrap 可能会与正在考虑的本地化、国际化等想法发生冲突。

  • ErrorFormatter并且ErrorPrinter还没有得到更深入的使用并且有疣。平底船。


查看完整回答
反对 回复 2023-04-17
  • 2 回答
  • 0 关注
  • 84 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信