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数据库. ...
随机推荐
- 深度 | 新兴软件研发范式崛起,云计算全面走向 Serverless 化
11月3日,2022 杭州 · 云栖大会上,阿里云智能总裁张建锋表示,以云为核心的新型计算体系正在形成,软件研发范式正在发生新的变革,Serverless 是其中最重要的趋势之一,阿里云将坚定推进核心 ...
- Element-ui 之 form表单套数组、表单数组套数组的校验rules
https://blog.csdn.net/qq_61553794/article/details/135451461
- java基础-java面向对象-02-day09
目录 1. 封装 2. 继承 2.1 什么是方法的重写 2.2 super 2.3 object详解 2.4 equals方法 3. 多态 4. final修饰符 5.抽象类 6. 接口 7. 内部类 ...
- 设备共享分配:虚拟化和 SRIOV
SRIOV 简介 OpenStack 自 Juno 版本开始引入 SRIOV,SRIOV(Single Root I/O Virtualization) 是将 PCIe(PCI) 设备虚拟化成虚拟 P ...
- Kafka 面试题
1. 为什么要使用 Kafka,为什么要使用消息队列 1.使用消息队列的目的: 服务解耦 流量削峰 异步通信 在早期的 web 应用程序开发中,当请求量突然上来了时候,我们会将要处理的数据推送到一个队 ...
- ClickHouse的Join算法
ClickHouse的Join算法 ClickHouse是一款开源的列式分析型数据库(OLAP),专为需要超低延迟分析查询大量数据的场景而生.为了实现分析应用可能达到的最佳性能,分析型数据库(OLAP ...
- C# WPF:这次把文件拖出去!
首发公众号:Dotnet9 作者:沙漠之尽头的狼 编辑于:成都,2020-12-01 回顾上篇文章:C# WPF:把文件给我拖进来!!! 本文完成对应的下文:<C# WPF:这次把文件拖出去!& ...
- CS2打开可以听到声音,但黑屏问题?
1.问题 我这里原先是可以启动CS2的,但是后来在CS2中重新调整了分辨率等等,之后由于某种原因又调整了屏幕分辨率,导致后面一进入CS2登录界面,橙色登陆界面就会缩在左上角一小块,并且之后就会陷入黑屏 ...
- [转帖]Sql Server之旅——第六站 使用winHex利器加深理解数据页
https://www.cnblogs.com/huangxincheng/p/4251770.html 这篇我来介绍一个winhex利器,这个工具网上有介绍,用途大着呢,可以用来玩数据修复,恢复删除 ...
- [转帖]实战演练 | Navicat 数据生成功能
https://zhuanlan.zhihu.com/p/631823381 数据生成的目的是依据某个数据模型,从原始数据通过计算得到目标系统所需要的符合该模型的数据.数据生成与数据模型是分不开的,数 ...