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数据库. ...
随机推荐
- idea调式技巧汇总
调式进阶1: https://blog.csdn.net/future_god_qr/article/details/121250865 调式进阶2:(evaluate用法:)快捷键:alt+f8 ...
- Redis无法使用ip访问
问题: 启动redis服务,可以使用127.0.0.1配置并使用访问redis,但是换成IP地址就无法访问 127.0.0.1 可以 [root]# ./redis-cli -c -h 127 ...
- 移动端h5中rem适配
1 (function (win, lib) { 2 var doc = win.document; 3 var docEl = doc.documentElement; 4 var metaEl = ...
- P1077-DP【黄】
昨天好几道题没做出来很郁闷,结果今天上来半小时不到就直接做出一道黄DP题了,不错,又有写题的冲动了. 这道题我一直被那个"因为方案数可能很多,请输出方案数对 1000007取模的结果.&qu ...
- 在Python中使用Process创建子进程遇到的问题
假如使用Process创建子进程,那么在最后的函数调用时需要加上if __name__ == "__main__":语句,否则会报错. 未使用该语句 代码示例 from multi ...
- spring-transaction源码分析(2)EnableTransactionManagement注解
概述(Java doc) 该注解开启spring的注解驱动事务管理功能,通常标注在@Configuration类上面用于开启命令式事务管理或响应式事务管理. @Configuration @Enabl ...
- 【C++】类概念及使用
类定义中不允许对数据成员初始化 类外只能访问公有部分 类成员必须指定访问属性 类的成员函数是实现对封装的数据成员进行操作的唯一途径 类定义中不允许定义本类对象,因无法预知大小 类与结构形式相同,唯一区 ...
- Android Studio 的 Gradle 面板没有 Task
问题描述:Android Studio Gradle 窗口没有显示 task 列表的问题,如下图所示: 网上找了好久都没有找到原因,最后自己摸索,找了解决方法. 解决方法:依次点击:File -> ...
- Git-历史版本切换-log-reset
- TiKV占用内存超过的解决过程
TiKV占用内存超过的解决过程 背景 为了后去TiDB的极限数据. 晚上在每台服务器上面增加了多个TiKV的节点. 主要方式为: 每个NVME的硬盘增加两个TiKV的进程. 这样每个服务器两个磁盘, ...