Mygin之错误恢复Recover中间件
本篇是mygin这个系列的最后一篇。如果想自己动手实现一个类似Gin的Web框架,建议从 mgin第一篇开始,
总代码行数有效行数只有600多行

github源码 mygin
目的
- 实现错误处理机制
panic简介
在实现错误处理机制之前,我们知道在Go 中,错误的处理方式依靠return返回,由调用者处理。如果是不可恢复的错误,可以手动抛出错误,当然在实际运行中,也会遇到不可处理的错误,比如说除数为0的时候painc 也会触发。终止当前的程序。
手动触发错误
//main.go
func main() {
fmt.Println("before ....")
panic("some err message")
fmt.Println("after ....")
}
- shell
~ go run cmd/main.go
before ....
panic: some err message
goroutine 1 [running]:
main.main()
/var/www/gophp/cmd/main.go:12 +0x5f
exit status 2
程序发生错误
func main() {
i := []int{0}
j := 2
fmt.Println(j / i[0])
}
- shell
go run cmd/main.go
panic: runtime error: integer divide by zero
goroutine 1 [running]:
main.main()
/var/www/gophp/cmd/main.go:13 +0x194
exit status 2
painc 的介绍到此为止,也就是发生了painc 错误,对程序来说就是比较严重的,就会中断,但是这个时候需要捕获到这个painc 错误,Go中没有try...Catch...又想捕获错误,这个时候就该defer 出场了。
defer简介
panic会导致程序被中止,但是在程序终止前,会先把当前协程上已经defer的任务,执行完成后再终止。效果类似于其他语言的try...catch。
示例
//cmd/main.go
func main() {
defer func() {
fmt.Println("defer....")
}()
i := []int{0}
j := 2
fmt.Println(j / i[0])
}
- shell
~ go run cmd/main.go
defer....
panic: runtime error: integer divide by zero
goroutine 1 [running]:
main.main()
/var/www/gophp/cmd/main.go:16 +0x1d3
exit status 2
可以看到,在程序退出之前先执行了defer 中的函数。如果能在defer 中捕获painc 错误,那么就能实现其他语言的try...catch
recover函数
介绍之前看官方文档怎么定义的
// The recover built-in function allows a program to manage behavior of a
// panicking goroutine. Executing a call to recover inside a deferred
// function (but not any function called by it) stops the panicking sequence
// by restoring normal execution and retrieves the error value passed to the
// call of panic. If recover is called outside the deferred function it will
// not stop a panicking sequence. In this case, or when the goroutine is not
// panicking, or if the argument supplied to panic was nil, recover returns
// nil. Thus the return value from recover reports whether the goroutine is
// panicking.
翻译后...
recover 内置函数允许程序管理恐慌的 goroutine 的行为。在延迟函数(但不是它调用的任何函数)
中执行恢复调用会通过恢复正常执行来停止 panic 序列,并检索传递给 panic 调用的错误值。如果
在延迟函数之外调用 recover,则不会停止紧急序列。在这种情况下,或者当 goroutine 没有
panic 时,或者如果提供给 panic 的参数为 nil,则 recover 返回 nil。因此,recover 的
返回值报告 goroutine 是否处于恐慌状态。
应用
- cmd/main.go
func main() {
defer func() {
fmt.Println("defer....")
if err := recover(); err != nil {
fmt.Printf("recover 到错误信息:%s\n", err.(error).Error())
fmt.Println("recover success")
}
}()
i := []int{0}
j := 2
fmt.Println(j / i[0])
fmt.Println("after panic")
- shell
~ go run cmd/main.go
defer....
recover 到错误信息:runtime error: integer divide by zero
recover success
可以看到已经捕获到错误信息了,程序正常结束。 after panic 没有打印,这是正确的,当panic 被触发时,程序的执行栈就到定义的defer 函数。就像在try代码块中发生了异常,执行栈来到 catch,接下来执行 catch 代码块中的代码。而在 main() 中打印了 recover success,说明程序已经恢复正常,继续往下执行直到结束。
mygin的错误处理
对一个 Web 框架而言,错误处理机制是必要的。如果发生了painc错误,应当返回错误信息或告诉对方失败了,而不至于什么都不返回,对调用方十分不友好。
例如我有如下的逻辑
package main
import (
"fmt"
"github.com/scott-pb/mygin"
"net/http"
"strconv"
)
func main() {
r := mygin.Default()
group := r.Group("/api")
group.GET("/recovery/:index", func(c *mygin.Context) {
index := c.Params.ByName("index")
i, _ := strconv.ParseInt(index, 10, 10)
s := []int{1, 3, 5, 7, 9}
c.String(http.StatusOK, fmt.Sprintf("index:%d result:%d success!\n", i, s[i]))
})
err := r.Run(":8088")
if err != nil {
fmt.Println(err)
}
}
根据调用方传递的index下标函数数组中的值,当传递的index超过数组下标时,就会发生painc错误,如果执行/api/recovery/10 时,就会发生错误。调用方什么返回都没有。
这个时候,就需要添加一个错误的处理机制,当错误发生时,向调用方返回Internal Server Error,且打印必要的错误信息,方便进行错误定位。
实现中间件 Recovery
新增文件 gee/recovery.go,在这个文件中实现中间件 Recovery
package mygin
import (
"fmt"
"net/http"
)
// Recovery 发生错误时,恢复函数,且返回相应错误信息。
func Recovery() HandlerFunc {
return func(c *Context) {
// 使用defer延迟执行,以便在函数退出时进行recover
defer func() {
if err := recover(); err != nil {
// 如果发生panic,打印错误信息并返回500 Internal Server Error响应
fmt.Println(err.(error).Error())
c.Writer.Write([]byte("Internal Server Error\n"))
c.status = http.StatusInternalServerError
c.Abort() // 终止后续中间件的执行
}
}()
c.Next() // 调用下一个中间件或处理函数
}
}
Recovery 方法很简单,使用defer挂载上错误恢复的函数,在这个函数中调用 recover方法,捕获panic,打印错误信息,并且向调用方返回 Internal Server Error。
// Default 返回一个默认的引擎实例
func Default() *Engine {
engine := New()
//Logger Recovery 中间件
engine.Use(Logger(), Recovery())
// Group 保存 engine 的指针
engine.RouterGroup.engine = engine
return engine
}
接下来就是测试了
测试
- shell
~ curl http://127.0.0.1:8088/api/recovery/1
index:1 result:3 success!
~ curl http://127.0.0.1:8088/api/recovery/10
Internal Server Error
- 控制台输出

可以看到第一次请求返回200成功,第二次请求,先打印了错误信息,然后对应的返回500错误,且调用方接收到了Internal Server Error。
Mygin之错误恢复Recover中间件的更多相关文章
- Go语言 异常panic和恢复recover用法
Go语言 异常panic和恢复recover用法 背景:Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在 ...
- Android IOS WebRTC 音视频开发总结(七五)-- WebRTC视频通信中的错误恢复机制
本文主要介绍WebRTC视频通信中的错误恢复机制(我们翻译和整理的,译者:jiangpeng),最早发表在[这里] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:blac ...
- webrtc如何进行错误恢复
视频的压缩方法:(三种帧) 为了视频尽可能的保持高效,视频数据通过不同的编码进行压缩.以帧为单位进行压缩,按照压缩中的不同作用可分类为:内帧(Intra-frames,I帧),预测帧(Predicti ...
- linux dd实现磁盘完整全盘镜像备份backup,恢复recover(restore)
1,dd操作就是简单的按字节复制,什么分区表啊,MBR(master boot record)啊统统照搬; 1. 磁盘克隆 也就是把整个硬盘复制一份.当然你首先需要在计算机上在接上一块新硬盘,并让系统 ...
- 禁用 Windows 启动时错误恢复的“启动启动修复(推荐)”
bcdedit /set {default} bootstatuspolicy ignoreallfailures bcdedit /set {current} recoveryenabled No
- StatusCodePagesMiddleware中间件如何针对响应码呈现错误页面
StatusCodePagesMiddleware中间件如何针对响应码呈现错误页面 StatusCodePagesMiddleware中间件与ExceptionHandlerMiddleware中间件 ...
- ExceptionHandlerMiddleware中间件如何呈现“定制化错误页面”
ExceptionHandlerMiddleware中间件如何呈现“定制化错误页面” DeveloperExceptionPageMiddleware中间件利用呈现出来的错误页面实现抛出异常和当前请求 ...
- .Net Core中间件和过滤器实现错误日志记录
1.中间件的概念 ASP.NET Core的处理流程是一个管道,中间件是组装到应用程序管道中用来处理请求和响应的组件. 每个中间件可以: 选择是否将请求传递给管道中的下一个组件. 可以在调用管道中的下 ...
- 【原创】express3.4.8源码解析之中间件
前言 注意:旧文章转成markdown格式. 中间件(middleware)的概念来自于TJ的connect库,express就是建立在connect之上. 就如同connect的意思是 连接 一样, ...
- Oracle RMAN备份恢复指导书
目 录 1 目的与范围... 1 2 术语和定义... 1 3 角色和职责... 2 4 使用RMAN备份数据库... 2 4.1.1 检查数据库模式... 2 4.1.2 连接到target数据库. ...
随机推荐
- 八、docker-file自动构建docker镜像
系列导航 一.docker入门(概念) 二.docker的安装和镜像管理 三.docker容器的常用命令 四.容器的网络访问 五.容器端口转发 六.docker数据卷 七.手动制作docker镜像 八 ...
- eyebeam高级设置
概述 VOIP测试过程中,经常会用到各种各样的SIP终端,eyebeam是其中最常见的一种. 在eyebeam的配置option中,只有少量的配置选项,有些特殊的设置无法配置. 比如DTMF码的发码形 ...
- netstat 命令查看端口状态详解
转载请注明出处: netstat 可以查看服务器当前端口列表及指定端口的连接状态等: -t : 指明显示TCP端口,t是TCP的首字母. -u : 指明显示UDP端口,u是UDP的首字母 -p : 显 ...
- com.alibaba.fastjson.JSONException: create instance error
很早之前在使用FashJson进行实体类转化的时候,如果json参数是多层都是一层对应一个单独的实体类, 今天在项目中想,使用内部类是不是也可以实现,且使用内部类封装性更好.当将json串使用fast ...
- Oracle表结构&数据类型&约束
1.Oracle 表结构 1.1.创建表名的规范 推荐以"t_xxx" 不能含有"+- "等非法字符,eg:sql create table "t-1 ...
- Shell-基本
- linux环境C语言实现:h264与pcm封装成AVI格式
前言 拖了很久的AVI音视频封装实例,花了一天时间终于调完了,兼容性不是太好,但作为参考学习使用应该没有问题. RIFF和AVI以及WAV格式,可以参考前面的一些文章.这里详细介绍将一个H264视 ...
- canal报错nosuchmethod ..bytebuffer
解决方法:更新jdk版本与es一致
- [转帖]HBase实战:记一次Safepoint导致长时间STW的踩坑之旅
https://mp.weixin.qq.com/s/GEwD1B-XqFIudWP_EbGgdQ 过 程 记 录 现象:小米有一个比较大的公共离线HBase集群,用户很多,每天有大量的Ma ...
- OB指定开源版本MySQL模式单节点安装
OB指定开源版本MySQL模式单节点安装 yum源处理 yum install -y yum-utils yum-config-manager --add-repo https://mirrors.a ...