学会定制化 Go 项目的 error,回溯错误的原因和发生位置

开发 前端
如果你们的研发习惯是请求接口的响应一律是 HTTP 200 再通过相应里的code码判断是否正确,这个方法可以放着不用, 规范化一点肯定是这种比较好,况且HTTP Status 不是 200 状态码,也是可以返回 code msg 那些信息给客户端的。

Go语言的Error处理一直被人吐槽,吐槽的点除了一个接一个的 if err != nil 的判断外,还有人说Go的错误太原始不能像其他语言那样在抛出异常的时候的时候传一个Casue Exception 把导致异常的整个原因链串起来。

第一点确实是事实,但是写习惯了也能接受,而且对新手友好。第二点属实就有点尬黑了。

用Go开发项目时想让程序抛出的 error 信息不要那么单薄,需要自己搭建项目时先做一番基础工作,自己定义项目的Error类型在包装错误的时候记录上错误的原因和发生的位置,比如像下面这样。

{
    "code": 10000000,
    "msg": "服务器内部错误",
    "cause": "db error: undefined column user_id",
    "occurred": "go-study-lab/go-mall.TestAppError, file: building.go, line: 69"
}

同时它还要实现Go的error interface,能融入Go 的错误处理机制才行。

今天我就带大家通过自定义项目Error并实现 Go error interface ,让你的Go项目Error拥有更丰富的错误原因和发生位置的信息。看到一个错误能看出来时什么原因导致的、以及是哪的代码导致的这样能大大降低Go项目的维护难度。

Go Error 定制化Go Error 定制化

定义项目的Error结构

首先我们在项目的common目录中增加errcode目录,该目录下会创建两个文件error.go 和 code.go。error.go文件用来存放自定义Error的结构和相关方法,code.go 用来放置项目各种预定义的Error。

.
|-- common
|   |-- errcode
|       |---code.go
|       |---error.go
|-- main.go
|-- go.mod
|-- go.sum

我们现在error.go 中定义AppError

type AppError struct {
 code     int    `json:"code"`
 msg      string `json:"msg"`
 cause    error  `json:"cause"`
}

cause 字段保存的是导致产生 AppErr 的原因,比如一个数据库查询语法错误,拿它再来生成项目的 AppError 或者是给预定义好的 AppError 追加上这个原因的error, 这样就能达到保存错误链条的目的。

现在AppError 还不是 error 类型,需要让他实现Go的 error interface,这个接口如下。

type error interface {
 Error() string
}

其中只定义了一个方法,我们让AppError实现Error方法把它变成 error 类型。

func (e *AppError) Error() string {
 if e == nil {
  return ""
 }

 formattedErr := struct {
  Code     int    `json:"code"`
  Msg      string `json:"msg"`
  Cause    string `json:"cause"`
 }{
  Code:     e.Code(),
  Msg:      e.Msg(),
 }

 if e.cause != nil {
  formattedErr.Cause = e.cause.Error()
 }
 errByte, _ := json.Marshal(formattedErr)
 return string(errByte)
}

Error方法返回的是AppError对象的JSON序列化字符串,其中如果cause字段不为空即错误原因不为空,再去错误原因的Error方法拿到底层的错误信息。

我们把Stringer 接口也实现一下,在没有类型字段转换的地方,它还是*AppErr类型,保证这个时候序列化它的时候仍然能得到期望的信息。

func (e *AppError) String() string {
 return e.Error()
}

接下来我们在code.go 中先预定义一些基础的错误

var (
 Success            = newError(0, "success")
 ErrServer          = newError(10000000, "服务器内部错误")
 ErrParams          = newError(10000001, "参数错误, 请检查")
 ErrNotFound        = newError(10000002, "资源未找到")
 ErrPanic           = newError(10000003, "(*^__^*)系统开小差了,请稍后重试") // 无预期的panic错误
 ErrToken           = newError(10000004, "Token无效")
 ErrForbidden       = newError(10000005, "未授权") // 访问一些未授权的资源时的错误
 ErrTooManyRequests = newError(10000006, "请求过多")
)

上面大家看到了 AppError 的类型定义中,字段的访问性都是包内可访问的,所以我们要定义一些 getter 方法,这样接口返回错误响应时,才能读到错误码和错误信息。

func (e *AppError) Code() int {
 return e.code
}

func (e *AppError) Msg() string {
 return e.msg
}

func (e *AppError) HttpStatusCode() int {
 switch e.Code() {
 case Success.Code():
  return http.StatusOK
 case ErrServer.Code(), ErrPanic.Code():
  return http.StatusInternalServerError
 case ErrParams.Code():
  return http.StatusBadRequest
 case ErrNotFound.Code():
  return http.StatusNotFound
 case ErrTooManyRequests.Code():
  return http.StatusTooManyRequests
 case ErrToken.Code():
  return http.StatusUnauthorized
 case ErrForbidden.Code():
  return http.StatusForbidden
 default:
  return http.StatusInternalServerError
 }
}

这里的 HttpStatusCode 返回的是HTTP 状态码,如果你们的研发习惯是请求接口的响应一律是 HTTP 200 再通过相应里的code码判断是否正确,这个方法可以放着不用, 规范化一点肯定是这种比较好,况且HTTP Status 不是 200 状态码,也是可以返回 code msg 那些信息给客户端的。

底层Error怎么变成项目Error

上面我们预定义好了几个应用错误,这里说明一下,预定义好的错误会最终返回给发起请求的客户端,所以控制器层各个URI的路由处理控制器中最后一定要返回预定义的错误,这个我们会在未来给Go项目封装统一的响应组件时处理。

那一个底层的错误怎么才能变成我们自定义的错误呢?

责任编辑:武晓燕 来源: 网管叨bi叨
相关推荐

2024-10-30 09:29:30

Go项目Error

2022-11-09 11:50:21

2020-10-10 07:14:08

前端项目断点

2009-07-20 15:51:45

JDBC连接Oracl

2023-02-07 07:16:54

人工智能机器学习方法

2010-08-02 08:54:53

Flex模块化

2022-07-19 08:01:55

函数Go格式化

2020-08-07 11:46:47

JavaScript开发代码

2024-02-21 19:02:05

Go模板化方式

2021-01-25 10:30:52

数字化分析转型首席执行官

2022-10-09 14:50:24

前端pnpm工具

2023-09-07 07:53:21

JavaScriptGoRust

2018-06-12 15:55:44

数字化项目

2021-06-29 10:01:56

物联网项目eSIM物联网

2020-06-05 08:29:40

语言PythonGo

2023-07-12 15:41:18

2022-05-17 08:02:55

GoTryLock模式

2013-08-14 14:36:07

开源项目

2018-06-29 10:34:40

区块链数字货币比特币

2014-04-23 13:45:40

iOS项目目录结构开发流程
点赞
收藏

51CTO技术栈公众号