当前位置: 首页 > 科技观察

Go应用优雅处理错误的几个Tips

时间:2023-03-13 06:20:07 科技观察

Go语言非常强大,现在非常流行——很多项目都是用Go语言实现的,比如Kubernetes。Go语言的一个有趣特性是它的多值返回特性提供了一种不同于其他编程语言的错误处理方法。Go将错误视为具有预定义类型的值,它本身是一种接口类型。然而,编写更大的应用程序需要包含更多上下文信息的错误,而不仅仅是一个值。在本文中,我们将探讨如何包装Go的错误类型,从而为您的应用程序带来更大的价值。用户定义的类型我们将覆盖Go中的内置错误类型,从一个自定义错误类型开始,该类型将被识别为程序中的错误类型。因此,我们引入了一种新的自定义错误类型来包装Go的错误。typeGoErrorstruct{error}contextdata当我们说error在Go中是一个值时,它是一个字符串值——任何实现了Error()字符串函数的类型都可以被认为是错误类型。将字符串值视为错误会使跨层的错误处理变得复杂,因此处理错误字符串消息并不是正确的做法。所以我们可以解耦字符串和错误代码:typeGoErrorstruct{errorCodestring}现在根据错误代码Code字段而不是字符串来处理错误。让我们通过上下文数据进一步解耦错误字符串,其中可以使用i18n包进行国际化。typeGoErrorstruct{errorCodestringDatamap[string]interface{}}Data包含用于构造错误字符串的上下文数据。错误字符串可以通过i18N定义文件中的data://i18Ndef"InvalidParamValue":"Invalidparametervalue'{{.actual}}',expected'{{.expected}}'for'{{.name}}'"模板化在中,错误代码Code将映射到使用Data构造的模板化错误字符串。原因(Causes)错误可能发生在任何层,需要为每一层提供处理错误的选项,并进一步使用额外的上下文信息来包装错误而不丢失原始错误值。GoError结构体可以进一步封装Causes来保存整个错误栈。typeGoErrorstruct{errorCodestringDatamap[string]interface{}Causes[]error}如果需要保存多个错误数据,causes是一个数组类型,设置为基本错误类型,在程序中包含该cause的第三方错误。组件(Component)标记层组件将有助于识别错误发生在哪一层,并且可以避免不必要的错误换行。比如service层出现service类型的error组件,可能不需要wraperror。检查组件信息将有助于防止暴露不应通知用户的错误,例如数据库错误:library")响应类型(ResponseType)添加一个错误响应类型,这样它就可以支持错误分类,这样就很容易理解它是什么类型的错误。例如错误可以按照响应类型(如NotFound)进行分类,DbRecordNotFound、ResourceNotFound、UserNotFound等错误都可以归类为NotFounderrors。这在多层应用程序开发期间很有用,并且是可选的封装:少数情况下,将重试错误。retry字段可以通过设置Retryable标志来决定是否进行错误重试:typeGoErrorstruct{errorCodestringMessagestringDatamap[string]interface{}Causes[]errorComponentErrComponentResponseTypeResponseErrTypeRetryablebool}GoError接口可以通过定义一个带有GoError实现的显式错误接口来简化错误检查:、packagegoerrtypeErrorinterface{errorCode()stringMessage()stringCause()errorCauses()[]errorData()map[string]interface{}String()stringResponseErrType()ResponseErrTypeSetResponseType(rResponseErrType)ErrorComponent()ErrComponentSetComponent(cErrSetComponentReable)ErrorRetry)Error}抽象错误同上-提到封装方法,更重要的是对错误进行抽象,将这些封装存储在同一个地方,提供错误函数funcResourceNotFound(id,kindstring,causeerror)GoError{data:=map[string]interface{}的复用性{“kind”:kind,“id”:id}returnGoError{代码:“ResourceNotFound”,数据:数据,原因:[]error{cause},Component:ErrService,ResponseType:NotFound,Retryable:false,}}这个错误函数抽象了ResourceNotFound错误,开发者可以使用这个函数返回错误对象,而不是每次都创建一个新的对象://UserServiceuser,错误:=u.repo.FindUser(ctx,userId)iferr!=nil{iferr.ResponseType==NotFound{returnResourceNotFound(userUid,"User",err)}returnerr}结论在多层应用程序中更有意义。您可以在这个GitHubGist[1]中看到完整的代码实现和定义。