gin框架 自定义404错误页面,自定义500等服务端异常,业务异常,根据不同异常类型显示不同的异常页面方法 整理

news/2024/9/25 22:16:40/

        在gin框架中,要显示自定义的异常页面,首先需要通过gin路由对象中的LoadHTMLFiles或者LoadHTMLGlob方法加载自定义的错误页面模板文件, 然后定义符合 gin.HandlerFunc 类型的路由处理函数/方法 ,即只有一个参数(c *ginx.XContext)的函数或者方法。 404异常使用路由对象的.NoRoute方法绑定路由处理函数, 其他类型的异常采用在中间件中采用defer + recover()函数捕获异常,然后根据不同类型的异常显示不同的模板页面信息。

gin框架中自定义404错误页面

        gin框架中自定义404错误页面, 实际上就是没有路由匹配的页面,使用gin内置的路由对象中的方法 r.NoRoute(Xxx), 这里的r是r := gin.Default() 的对象。 Xxx 就是一个 HandlerFunc 函数类型定义,即函数/方法的参数定义是 func(*Context) ,如: func ErrHandler404(c *ginx.XContext) { }

自定义404错误页面示例:

html模板页面 templates/errors/404.html

{{define "errors/404.html"}}
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{.title}}</title>
</head><body><h1>{{.title}}</h1>
</body></html>
{{end}}

 404异常页面HandlerFunc处理函数

// 自定义404异常页面处理器函数
func ErrHandler404(c *gin.Context) {c.HTML(200, "errors/404.html", ginx.H{"title": "404 Error - Page not found"})
}

404异常模板文件加载和路由绑定


func main() {r := gin.Default()// 模式匹配方式加载html模板文件r.LoadHTMLGlob("templates/**/*.html")// 指定模板文件加载//r.LoadHTMLFiles("templates/errors/404.html", "templates/errors/500.html")// 自定义404异常页面处理路由绑定r.NoRoute(ErrHandler404)// 路由绑定r.GET("/", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"title": "Hello world!",})})r.Run(":8080")
}

启动服务, 访问 http://localhost:8080/abc  这时就会显示我们自定义的404异常页面

gin框架根据不同的异常类型显示不同的异常页面的方法

gin框架根据不同类型的异常显示不同的异常页面采用在中间件中采用defer + recover()函数捕获异常,然后根据不同类型的异常显示不同的模板页面信息。 

这个也是需要先加载模板文件的,方法和上面的一样

html异常模板文件定义

 templates/errors/500.html   其他模板文件定义省略, 注意这里的模板文件名称定义 ,即 {{define "模板文件名"}} 

{{define "errors/500.html"}}
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{.title}}</title>
</head><body><h1>{{.title}}</h1><h2>{{.msg}}</h2>
</body></html>
{{end}}

defer + recover()异常处理中间件定义

defer + recover()函数, 然后在使用  switch err := recErr.(type) { case xxx: } 的形式根据不同的异常类型显示不同的html模板和数据。

下面的switch case里面就是我们要捕获的异常类型,  case的类型就是我们通过panic获取其他程序抛出的异类型, 如 panic("Demo Error ") 这个是一个string类型的异常,在case里面就会被string分支捕获;这个异常  panic(&net.OpError{Op: "read",Net: "tcp", Err: fmt.Errorf("demo for op error")}) 就会被 case *net.OpError: 分支捕获。 其他的以此类推.....


// 服务器异常处理中间件
func ErrHandlerMiddleware(c *gin.Context) {// 注意这里通过defer语句+recover()函数来捕获异常defer func() {// 注意这里的recover()返回的异常类型是interface{}类型,可以通过类型断言来判断是何种类型的异常if recErr := recover(); recErr != nil {tpl := "errors/500.html" // 默认异常模板文件data := make(map[string]interface{})// switch + type类型推断switch err := recErr.(type) {case *net.OpError:tpl = "errors/net_error.html"data["title"] = "net.OpError exception has occurred"data["msg"] = fmt.Sprintf("%v", err.Error())case *net.AddrError:tpl = "errors/net_error.html"data["title"] = "net.AddrError exception has occurred"data["msg"] = fmt.Sprintf("%v", err.Error())case string:data["title"] = errdata["msg"] = errdefault:// 模板数据data["title"] = errdata["msg"] = err}global.Log.Errorf("error: %v", data)//debug.PrintStack() //打印错误堆栈信息// 显示自定义错误页面c.HTML(200, tpl, data)}}()// 继续后续的处理 这个是中间件和 路由处理控制器的重要区别c.Next()
}

gin使用示例

这个方式使用的时候只需要在gin路由对象里面通过Use方法加载这个中间件即可,即:

r.Use(ErrHandlerMiddleware)

在控制器中如果有异常发生,就会被我们定义的中间件捕获, 然后我们就可以在中间件中根据不同的类型设置不同的异常模板页面。

控制器抛异常示例


var IndexCtr = cIndex{}type cIndex struct{}func (a *cIndex) DemoErr1(c *gin.Context) {// 抛一个异常panic("This is not implemented for Demo Error purpose.")
}
func (a *cIndex) DemoErr2(c *gin.Context) {// 抛一个异常panic(fmt.Errorf("fmt error demo2, gin Request: %v", c.Request))
}
func (a *cIndex) DemoErr3(c *gin.Context) {// 抛一个异常 *net.AddrErrorpanic(&net.OpError{Op: "read",Net: "tcp", Err: fmt.Errorf("demo for op error")})
}
func (a *cIndex) DemoErr4(c *gin.Context) {// 抛一个异常 *net.AddrErrorpanic(&net.AddrError{Err: "fmt error demo2", Addr: "localhost"})
}

gin路由 HandlerFunc处理函数/方法 和gin中间件的区别

gin框架中, 路由的处理函数和中间件的参数定义都是一样的,都必须符合HandlerFunc的类型定义,即: type HandlerFunc func(*Context)

他们的区别在于gin中间件中 使用 c.Next() 方法来处理后续的网络请求。 在路由函数和中间件中如果要中断后续的请求,都需要调用 c.Abort() 方法来中断后续的调用链HandlersChain(这个其实就是一个HandlerFunc切片,他的定义是:type HandlersChain []HandlerFunc   ) 的请求。 而如果要中断当前函数的请求,则都需要使用 return语句。

总结: gin框架中的自定义异常页面实际上就是一个路由绑定和异常捕获和异常类型断言的过程,其中404异常他就是一个无路由异常,需要使用路由对象的 NoRoute方法绑定对应的异常处理函数, 其他类型的异常需要使用 中间件 +defer + recover()的方式来处理不同类型的异常。 在gin 框架中还有一个 NoMethod的路由异常,这个异常在 Engine.HandleMethodNotAllowed = true时触发,使用方法和NoRoute一样,绑定一个HandlerFunc函数即可。


http://www.ppmy.cn/news/1505925.html

相关文章

英特尔凌动® P5300 和 P5700 处理器使企业能够优化现代网络基础架构、安全加速器和存储设备之间的性能和成本平衡。

介绍英特尔凌动 P5300 和 P5700 处理器 英特尔凌动处理器提供核心数和硬件功能各异的多种配置&#xff0c;用于支持不同的边缘用例。基于 10 纳米工艺的先进微架构与一组强大的加速器相结合&#xff0c;带来卓越的每核性能和先进的数据包处理能力。这些平台基于高能效的系统级…

TCL 实业 x TiDB丨从分销转向零售,如何考虑中台建设和数据库选型?

导读 在数字化转型的浪潮中&#xff0c;TCL 实业通过“新方舟”项目构建统一中台&#xff0c;实现了从分销向零售的转型&#xff0c;显著提升了业务精准度和效率。本文根据 InfoQ 记者高玉娴对 TCL 实业企业架构部架构师蔡玖发的采访整理&#xff0c;揭秘了 TCL 实业在这一转型…

【iOS】AutoreleasePool自动释放池的实现原理

目录 ARC与MRC项目中的main函数自动释放池autoreleasepool {}实现原理AutoreleasePoolPage总结 objc_autoreleasePoolPush的源码分析autoreleaseNewPageautoreleaseFullPageautoreleaseNoPage autoreleaseFast总结 autorelease方法源码分析objc_autoreleasePoolPop的源码分析po…

计算数学精解【3】-C++计算基础(3)

文章目录 运算,- 加减法* / 乘除法逐元 乘法逐元 除法逐元综合运算矩阵乘法与加减法 转置、共轭、伴随矩阵点乘法,叉积 大整数GMP概述GMP安装 [cygwin](https://cygwin.com/install.html)安装 gmpexample Eigen基本属性和运算 读写文件概述example csv读文件读取每个字段读取机…

策略模式的一次应用

项目的需求是将一组图像按照相似度分类。 采用了模板匹配计算相似度的实现方式。 #include <opencv2/core.hpp> #include <openev2/core/utility.hpp> #include <opencv2/highqui.hpp> #include <openav2/imgproc.hpp> cv::Mat image matched; double …

玩转Python爬虫中的正则表达式:从原理到实战!

&#x1f538; 正则表达式原理概述 正则表达式&#xff08;Regular Expression&#xff0c;简称RE&#xff09;是一种用于匹配文本的强大工具。它通过一些特殊字符&#xff08;称为元字符&#xff09;和模式&#xff0c;能够在文本中进行搜索、替换和解析操作。Python中的re模…

【第十二天】Redis相关知识

文章目录 什么是 redisredis 可以用来干什么1.缓存2.计数器3.排行榜4.社交网络5.消息队列6.分布式锁 redis 有哪些数据结构1. string2.hash3.list4.set5.sorted set redis 为什么快&#xff1f;redis 持久化机制有哪些RDB&#xff1a;AOF: RDB 与 AOF 的优缺点比较。什么是缓存…

【信创】Linux如何增加与删除交换分区 _ 统信 _ 麒麟 _ 中科方德

原文链接&#xff1a;信创】Linux如何增加与删除交换分区 | 统信 | 麒麟 | 中科方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在信创终端操作系统上如何增加与删除交换分区的文章。交换分区&#xff08;Swap&#xff09;在Linux系统中扮演着重要角色&…