比如数据库服务吧。
程序启动,先连 db 。连成功。启动 web 服务。然后 setup 路由啥的一堆。
好,服务启动成功了。
现在接受 http 请求,此时数据库崩了。
gorm 返回了 err 。比如代码如下
// userRepository.go
func GetUser(uid int64) (*User, error) {
user:=new(User)
if err :=db.model(user).Find(user).err; err != nil {
return nil,err
} else { return user, nil }
}
按照 java/php 这种的逻辑。我可以抛出个异常。然后有个地方是处理这个特殊的异常。返回 500,db no connection 。
go 里边咋做呢?现在数据库崩了以后,被业务中间件拦截到了 返回 401 unauthorized 。
repository 由 http 服务调用。我要直接 panic 吗 0.0 http 的中间件 recover 住判断 err 是哪种错误? 这么粗暴的吗?
1
voidmnwzp 1 天前 via iPhone
跟 java 一样向上抛啊
|
2
sduoduo233 1 天前 via Android
我一般是返回 500 ,然后记录一下错误
u,err:=GetUser(1) if err!=nil { w.WriteHeader(500) log.Println(err) return } |
3
dzdh OP |
4
laikick 1 天前
Panic + Recover + 自定义错误类型 就可以实现类似 Java 的操作 不过也仅仅是类似. 但是,这不是 go 推荐的.
别把 Java 的习惯带入 go 里面. 这两个语言的思想差别很大的. 你应该定义一个 ErrDBConnection. 然后在 http 服务 errors.Is(err, repository.ErrDBConnection). |
5
kk2syc 1 天前
可以直接 panic ,可以用中间件捕获。不一定比全局优雅,但是也是不错的,业务上我都这样。
---- func ExceptionMiddleware(c *gin.Context) { defer func() { if err := recover(); err != nil { c.JSON(500, gin.H{"msg": "ERROR"}) c.Abort() } }() c.Next() } |
6
kingcanfish 1 天前
java 有个地方处理这个特殊异常,go 中间件不就是这个特殊的地方吗
我一般是自己定义一些 error 比如 db error, logic errr, client err, 在最先抛出 error 的地方包一层, 然后网上传 最终中间根据不同的 error 返回错误码 ,比如你这个例子, 发现 db 包 error 了 我就包一层 自己的 db error ,往上抛, 中间件捕获 |
7
dzdh OP @kingcanfish 抛就是 panic 呗?
|
8
realpg 1 天前
还是不建议这么跨舒适区干活
玩惯了 exception 那一套 改 go 推荐这一套 可能一年都改不过来 不如再找个 java 工作 |
9
kingcanfish 1 天前
@dzdh #7 error
|
10
lasuar 1 天前
改不过来习惯 ,不建议写 go ,难受自个。编程范式都不一样了。
|
12
afxcn 1 天前
repository 返回正确的错误类型就好了。
|
13
Blackbelly 1 天前 2
直接 panic
因为数据库崩了属于 unrecoverable 的错误。这时候当成 err 向上抛没有意义,上一层也无法处理,只能层层往上抛。 而且,你的接口语义是 GetUser ,本身就不应该返回一个接口语义之外的错误。 按照接口语义,应该是返回一个 user ,或者是 NotFoundErr ,除此之外的错误都不应该返回。 |
14
chen11 23 小时 42 分钟前
我上周才遇见个 bug ,go 程序直接崩溃,log 没打出来,找不到 bug 在哪里。习惯了 java ,来写 go 就难受
|
15
zsj950618 23 小时 40 分钟前
> 被业务中间件拦截到了 返回 401 unauthorized
那是这个中间件垃圾,都不看错误类型就一股脑返回 401 。 |
16
chevalier 23 小时 19 分钟前
从功能上来说
Go 的 error 相当于 Java 的异常 Go 的 panic 相当于 Java 的 Error |
17
changz 22 小时 48 分钟前 via Android
用 protobuf 定义错误码,一层一层往上抛
|
18
wangritian 22 小时 34 分钟前
所有语言的最佳实践,都可以在流行框架内找到,推荐到 goframe 看看
go 一般是自己设计一个符合 error 接口(包含 Error 方法)的带 code 和 msg 的自定义 error 通过 recover 全局拦截异常,如果是底层报错(数据库连接失败等业务层无须接收),直接 panic 如果是业务异常(用户名重复),return 自定义 error |
19
guanzhangzhang 22 小时 25 分钟前
返回 error ,上层处理和家 warp 信息,最后到你接口层面你可以返回 500
|
20
leonshaw 22 小时 21 分钟前 via Android
我一般只有断言失败才用 panic ,其它情况都是返回 error ,视情况包装一层。
|
21
lqs 21 小时 49 分钟前
ctx 要一路带进去(适当的地方加上 WithTimeout / WithCancel 等)
err 要一路带出来(适当的地方包裹一下加上错误提示和相关参数上下文) |
22
iyaozhen 20 小时 20 分钟前
学 go 有个段子:和你们这些搞 java 的讲不清楚
|
24
Ghostisbored 18 小时 23 分钟前
每次看到一些回复都看得好笑 用个开发语言还用出优越感来了。php 流行的时候: 有新人问问题 你不适合 php 还是回去吧 ; java 流行的时候:你不适合 java 还是回去吧 ; go 近几年因为云原生 加上国内字节用的多 不少项目开始用了 又开始了:你不适合 go 还是去搞 java 吧 ; rust 现在被大家喜欢很多软件开始锈化 是不是过一段时间 要出现 你不适合搞 rust 快回去搞 go 吧🙄
|
25
rower 17 小时 38 分钟前
1.有处理错误的中间件,有处理 panic 的中间件,这两个是不一样的
2.这里是错误类型,我们走处理错误的中间件 3.你想返回的 http 状态码是 500 ,同时错误信息是 db no .... 却被 401 处理了 对于这种情况是创建自定义错误类型,参考 https://github.com/ardanlabs/service6-video/tree/main/app/api/errs ``` // Error represents an error in the system. type Error struct { Code ErrCode `json:"code"` Message string `json:"message"` } ``` 这里的 message 就是我们的错误信息`db no ...` 这里的 code 是我们内部错误的编码,比如说授权错误,code = 1 ,数据库错误,code = 2 参考 code 设计 https://github.com/ardanlabs/service6-video/blob/main/app/api/errs/codes.go 4.如何将 不同的错误转换为对应的 http 状态码,需要建立 code 和 http 状态码的 map 关系 参考 https://github.com/ardanlabs/service6-video/blob/main/api/http/api/mid/errors.go 5.错误中间件处理错误 这里处理错误时,如果是我们自定义的错误,就将 code 转换为对应的 http 状态码,错误信息不变。 如果不是自定义错误,表明是未知错误,返回 {500,unknow err} 参考 https://github.com/ardanlabs/service6-video/blob/main/app/api/mid/errors.go ``` func Errors(ctx context.Context, log *logger.Logger, handler Handler) error { err := handler(ctx) if err == nil { return nil } log.Error(ctx, "message", "ERROR", err.Error()) // 这里判断是不是自定义错误 if errs.IsError(err) { return errs.GetError(err) } return errs.Newf(errs.Unknown, errs.Unknown.String()) } ``` |
26
z1829909 17 小时 27 分钟前
每一层调用处理能处理的 error, 不能处理的加一些额外信息返回上一层
异常其实也差不多, 只不过你自己不需要手动处理, 默认是往上抛的 代码里尽量不要 panic, 程序启动的时候, 做一些必要的依赖检测, 如果一些关键依赖缺失或者启动异常可以 panic |
27
povsister 17 小时 23 分钟前
和你们这些搞 java 的说不清楚.jpg
(狗头保命 |
28
bli22ard 17 小时 23 分钟前 via iPhone
java 是 try catch ,只需要处理 exception 。go 里面有两种一种是 panic ,一种是 error ,panic 通过 recover 捕获,error 就是带在函数返回值里面返回。可能是最佳实战的做法是,你调用了标准里,或者第三方库,这些库返回 error 之后,你应该先用一些第三方 error 库 wrap 一下,主要目的是记录一下 error 发生的调用栈,这样上层什么位置拿到 error ,都能打印出来这个 error 是哪个位置发生的。还有一种,目的类似的做法,不记录调用栈,而改为附加一个错误码进去,这样上层的任何调用者也可以知道 error 哪里发生的,不过维护错误码这种方式维护时间越长,越容易搞混乱,导致排查问题困难。
楼上有说,将 error 转换 panic 的做法,这样做看起来比较爽,不用多余定义 error 返回值,不用 if err!=nil 判断,只需调用入口 recover 住,但是这种做法,不是主流做法,至于为什么不主流,知道的可以解释下🤭。 |
29
haierspi 17 小时 18 分钟前
直接 Recover 返回 API 啊
|
30
soul11201 16 小时 56 分钟前
换个思路看下这个问题:调用栈比较深的时候,是否强制程序员显示处理流程异常终止情况。
1. PHP 是不强制的,在调用处看不出来处理程序可能出错。如果不 Catch 一个 Exception 的基类,可能导致异常漏处理;;但是写的很多,代码就很啰嗦、过度防御 。两个方面不管从哪个角度来说,都是工程质量堪忧。 2. Go err 最佳实践,其实就是强制显示处理错误,调用处就能看出来程序可能处理错误,整体看上去就是做 err 体操,有些呆板。 3. Java Exception 的 Checked Exception 和 Runtime Exception 设计思路,分别是 Go err 体操路子和 PHP 的埋雷路子。Checked Exception 相比 err 体操只不过是标记在了方法签名处,看上去代码少写了两行,但 Checked Excption 是从哪里丢出来的就不太明确了。从 Checked Exception 的实际使用情况来看,偏 Runtime Exception 方向倾斜,就是逐层标记不处理的多,垃圾~ 从设计和实际使用综合来看,还是 Go Err 相对来说更严谨一点 |
32
henix 6 小时 41 分钟前
我的话这种情况不会使用 panic / recover ,那个是给意料之外的严重异常用的
这种确实需要一路 return nil, err 如果需要中间处理,那在最开始创建 err 的时候选择一个特定类型,中间件用 errors.Is 判断 以上是如果你用网上的常用框架的话就这么做 我个人认为这些框架的错误处理设计得不好,我开发自己的 web 项目的时候不用任何框架,只用 go 标准库 我设计的 controller 会返回:(结果, err1, err2) 其中 err1 代表用户输入错误,比如参数检查错误,要给用户返回 4xx err2 代表服务器内部错误,要给用户返回 5xx 用这种方法,不需要 errors.Is 判断类型,只需要判断 err2 != nil 即可 |
33
fovecifer 5 小时 8 分钟前
我的建议是要慎重使用 panic ,切记
|