Golang web 项目中实现自定义 recovery 中间件

为什么需要实现自定义 recovery 中间件?

在 Golang 的 Web 项目中,自定义 recovery 中间件是一种常见的做法,用于捕获并处理应用程序的运行时错误,以避免整个应用程序崩溃并返回对应格式的响应数据。

很多三方 web 框架(例如 gin、echo)都提供了官方实现的 recovery 中间件,但是官方实现的中间件并不一定能满足自己的需求。例如 gin 官方提供的 recovery 中间件,发生 panic 后会将当前请求的标准状态码置为 500,body 置为空。但是这样的返回数据与格式可能会和自己的项目要求不一致。例如,项目发生 panic 后是要求标准状态码依然返回 200,body 值为 {"code":-1, "data":nil,"msg":"xxx"},这种场景下,就需要实现自己的 recovery 中间件了。

如何实现自定义 recovery 中间件?

如果使用 gin 框架的话,就非常简单了,因为 gin 提供了完善的中间件功能,遵守 gin 的要求实现满足自己项目的功能就可以了,简单示例代码如下:

复制代码
package main

import (
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"runtime"
)

func Recovery() gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				const size = 64 << 10
				stack := make([]byte, size)
				stack = stack[:runtime.Stack(stack, false)]
				log.Printf("[GinPanic] %s\n", string(stack))
				c.JSON(http.StatusOK, struct {
					Code int         `json:"code"`
					Data interface{} `json:"data"`
					Msg  string      `json:"msg"`
				}{
					Code: -1,
					Data: nil,
					Msg:  "server panic",
				})
				c.Abort()
			}
		}()
		c.Next()
	}
}

使用示例如下:

复制代码
package main

import (
	"github.com/gin-gonic/gin"
	"runtime"
)

func main() {
	r := gin.New()
	r.Use(Recovery())
	r.GET("/test", func(c *gin.Context) {
		panic("Oops! Something went wrong.")
	})
	r.Run(":8080")
}

运行起来后,访问 /test 触发 panic 后返回了预期的结果,如下:

复制代码
$ curl http://127.0.0.1:8080/test
{"code":-1,"data":null,"msg":"server panic"}

接下来再看一个基于原生包 net/http 的一个示例,代码如下:

复制代码
package main

import (
	"fmt"
	"log"
	"net/http"
	"runtime/debug"
)

// 自定义的recovery中间件
func recoveryMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				// 打印错误信息
				log.Println("[Recovery] Panic:", err)

				// 打印堆栈跟踪信息
				log.Printf("[Recovery] Stack Trace:\n%s\n", debug.Stack())

				// 返回一个适当的错误响应给客户端
				fmt.Fprintf(w, `{"code":-1,"data":null,"msg":"server panic"}`)
			}
		}()
		// 继续处理下一个中间件或路由处理函数
		next.ServeHTTP(w, r)
	})
}

// 示例的处理函数
func helloHandler(w http.ResponseWriter, r *http.Request) {
	panic("Oops! Something went wrong.") // 模拟一个错误
	w.Write([]byte("Hello, Recovery Middleware!"))
}

func main() {
	// 创建一个多路复用器
	mux := http.NewServeMux()

	// 注册中间件
	mux.Handle("/test", recoveryMiddleware(http.HandlerFunc(helloHandler)))

	// 创建服务器
	server := &http.Server{
		Addr:    ":8080",
		Handler: mux,
	}

	// 启动服务器
	log.Println("Server is running on http://localhost:8080")
	log.Fatal(server.ListenAndServe())
}

小结

Web 应用程序在运行时遇到错误并抛出 panic 时,自定义的 recovery 中间件将会捕获panic 并记录对应的错误和堆栈信息,避免应用程序崩溃,并向客户端发送适当的错误响应数据。对于文本的示例,可以根据自己的实际需求进行调整和扩展来实现自定义的 recovery 中间件。

相关推荐
Hello--_--World4 分钟前
ES13:类私有属性和方法、顶层 await、at() 方法、Object.hasOwnProperty()、类静态块 相关知识点
开发语言·javascript·es13
Hugh-Yu-13012312 分钟前
二元一次方程组求解器c++代码
开发语言·c++·算法
weixin_5206498718 分钟前
C#进阶-特性全知识点总结
开发语言·c#
文祐19 分钟前
C++类之虚函数表及其内存布局
开发语言·c++
亦暖筑序32 分钟前
Spring AI Alibaba 报错合集:我踩过的那些坑
java·后端
编程大师哥36 分钟前
C++类和对象
开发语言·c++·算法
M1582276905538 分钟前
工业 CAN 总线无线互联利器|4 路 CAN 转 4G/WiFi 网关 产品介绍
开发语言·php
石榴树下的七彩鱼41 分钟前
OCR 识别不准确怎么办?模糊 / 倾斜 / 反光图片优化实战(附完整解决方案 + 代码示例)
图像处理·人工智能·后端·ocr·api·文字识别·图片识别
burning_maple1 小时前
AI 工程实战指南:从零开始构建 AI 应用
开发语言·人工智能
你的牧游哥1 小时前
Java 核心概念详解
java·开发语言