1、GO学习之Hello World
2、GO学习之入门语法
3、GO学习之切片操作
4、GO学习之 Map 操作
5、GO学习之 结构体 操作
6、GO学习之 通道(Channel)
7、GO学习之 多线程(goroutine)
8、GO学习之 函数(Function)
9、GO学习之 接口(Interface)
10、GO学习之 网络通信(Net/Http)
11、GO学习之 微框架(Gin)
按照公司目前的任务,go 学习是必经之路了,虽然行业卷,不过技多不压身,依旧努力!!!
说起微服务框架,首当其冲的就是 JAVA 技术栈的 Spring Cloud 全家桶,吃过的人都说好,比较 Spring Cloud 已经可以说是完全能满足互联网后端开发需求,用 Spring Cloud 来搭建一个三高(高并发、高可用、高性能)的后端架构并非难事。
Spring Cloud:Zuul(路由)、Gateway(路由)、Eureka(注册中心)、Hystrix(熔断限流)、Config(配置中心)、Bus(事件,消息总线)、Sleuth(日志收集,链路追踪)、Ribbon(负载均衡)、OpenFeign(API 调用) 等。
Spring Cloud Alibaba:Nacos(注册配置中心)、Sentinel(限流降级)、Seata(分布式事务)、Stream(分布式消息)等。
那 Go 语言自然也会包容微服务的思想,Gin 就是 Go 语言中的一个微服务框架,是一个轻量级的、高性能的 Web 框架,为专门构建快速的 Web 应用和 API 而设计。
当然也有不足之处:
在使用 Gin 框架之前,首先需要安装 Gin 包,就类似 JAVA 中如果要用到 web 模块就需要映入 starter-web 模块。
通过如下命令来安装:
go get github.com/gin-gonic/gin
下面是一个基于 Gin 写的一个 简单的 API 示例。
示例中,首先导入 “github.com/gin-gonic/gin” 包,并使用 gin.Default() 来创建一个默认的 Gin 引擎 router。然后使用 router 来定义了一个 路由,使用匿名函数来处理响应,返回了一个 Hello Gin 的相应字符串。最后使用 router.Run() 启动这个 HTTP 服务。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个 Gin 引擎
router := gin.Default()
// 定义路由和处理函数,这里使用匿名函数
router.GET("/", func(ctx *gin.Context) {
ctx.String(http.StatusOK, "Hello Gin!!!")
})
// 启动 HTTP 服务器
router.Run("127.0.0.1:8080")
}
我们使用 go run hello.go 来运行,浏览器访问测试:
路由是指根据客户端请求的 URL 路径,将请求映射到相应的处理函数上,这里对比 SpringMVC 框架的 DispatcherServlet 控制中心。在 Web 应用中,当用户访问不同的 URL 路径时,应用程序就需要调用相对于的处理函数来处理用户的请求,每个请求的处理函数都有不同的业务逻辑,
路由就会帮你将不同的请求分配到正确的处理函数上
。
在 Gin 框架中,你可以使用不同的 HTTP 方法(GET, POST, PUT, DELETE等)来定义不同的路由,并且为每一个路由指定一个处理函数。
Gin 框架中采用的路由是基于 httprouter 做的。
下面来看一个小小的示例:
下面示例中,通过 Gin 框架定义了 /hello GET 接口和 /add POST 接口,并且从请求体中获取 name 参数。
我来来看,一个包中,我们可以定义不同路由已完成不同的请求操作。
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个 Gin 引擎
router := gin.Default()
// 定义路由和处理函数,这里使用匿名函数
router.GET("/hello", func(ctx *gin.Context) {
// 从请求体中查询 name 参数
name := ctx.Query("name")
fmt.Printf("接收到参数:%s", name)
ctx.String(http.StatusOK, "Hello Gin! %s", name)
})
router.POST("/add", func(ctx *gin.Context) {
var requestData struct {
Name string "json:name binding:required"
}
// 使用 ShouldBindJSON 方法将请求体中的 JSON 数据绑定到结构体中
if err := ctx.ShouldBindJSON(&requestData); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error:": err.Error()})
return
}
// 从结构体中获取参数
name := requestData.Name
if len(name) == 0 {
fmt.Println("获取name参数失败!")
ctx.String(http.StatusBadRequest, "add fail!")
} else {
fmt.Printf("添加操作:%+v 完成\n", name)
ctx.String(http.StatusOK, "%s add successful!", name)
}
})
// 启动 HTTP 服务器
router.Run("127.0.0.1:8080")
}
我们 Postman 来请求测试:
/hello GET 接口:
/add POST 请求:
在实际开发中,路由和函数处理运用非常广泛,以下是一些例子:
优点:
缺点:
在 Gin 框架中,请求和响应处理是开发 Web 应用的核心部分,也是程序猿日常做的最多的事情。
每开发一个API,逃不过这几步骤:
id := ctx.Param("id")
name := ctx.Query("name")
在获取 RequestBody 参数是,我们需要封装结构体,然后绑定请求体到结构体中。
var requestData struct {
Name string "json:name binding:required"
}
// 使用 ShouldBindJSON 方法将请求体中的 JSON 数据绑定到结构体中
if err := ctx.ShouldBindJSON(&requestData); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error:": err.Error()})
return
}
// 从结构体中获取参数
name := requestData.Name
在路由处理函数中,你可以执行你想要的处理逻辑。例如,你可以查询数据库、调用其他函数、处理数据等等。处理请求的逻辑在路由处理函数中实现,声明处理函数或者使用匿名函数来处理。
目前大多数的 Web 应用开发或者是系统之间相互调用、传参、接受数据等大多都是 JSON 格式数据,那在 SpringMVC 框架中有做封装,那在 Gin 框架中也是如此。
下面的案例中,我们通过
ctx.JSON()
方法向前端以 json 格式把数据返回,你可以将响应的数据封装成一个gin.H(类似 map)
对象,然后使用ctx.JSON
方法将其作为响应的主体返回给客户端。
注意
: 这里碰到一个小坑,忘记了 Go 语法中,小写是没有导出权限的,所以导致 postman 访问,data 中总是 [] 的,询问 大佬(chatGPT)才知道其中之奥妙,需大写。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个 Gin 引擎
router := gin.Default()
// 创建一个 Get 请求
router.GET("/test/:id", func(ctx *gin.Context) {
id := ctx.Param("id")
name := ctx.Query("name")
data := struct {
// 注意,这里定义字段一定要大写,要不然则不能导出,返回空
Id interface{} `json:"id"`
Name string `json:"name"`
}{
Id: id,
Name: name,
}
ctx.JSON(http.StatusOK, gin.H{"code": 200, "message": "请求成功", "data": data})
})
// 启动服务
router.Run(":8080")
}
在 postma 中测试
调用:http://127.0.01:8080/test/10?name=phen
响应:
{
"code": 200,
"data": {
"id": "10",
"name": "phen"
},
"message": "请求成功"
}
当然,如果需要返回其他类型的响应,比如 HTML页面、纯文本等,可以使用
ctx.HTML
、ctx.String
等方法,根据需要,设置适当的状态码和响应头。
Middleware
)是一种常见的软件设计模式,在 Web 开发中用于请求和响应之间添加自定义逻辑。SpringMVC
框架的话,就会知道 Filter 过滤器
,Filter 有 Pre-proces 和 Post-process 操作,Gin 框架的中间件和 Filter 差不多。中间件的作用对于请求和响应的进行预处理、后处理和记录,以便实现如下功能(不限于):
如何使用中间件呢:
在上面的案例中加入了中间件的使用,在下面的案例中,定义了一个
Logger()
函数来记录请求日志,在 Logger() 函数中,首先记录请求请求进入时间,调用Next()
函数 继续处理请求 最后记录结束记录时间和耗时。
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// 定义中间件处理函数,用户记录日志
func Logger() gin.HandlerFunc {
// 使用自定义函数来处理
return func(ctx *gin.Context) {
fmt.Println("Middleware Logger Handler...")
start := time.Now()
// 调用 Next 继续处理请求
ctx.Next()
end := time.Now()
latency := end.Sub(start)
log.Printf("[%s] 请求方法:%s 请求路径:%s 耗时:%v", end.Format("2006-01-02 15:04:05"), ctx.Request.Method, ctx.Request.URL.Path, latency)
}
}
func main() {
// 创建一个 Gin 引擎
router := gin.Default()
// 添加自定义的 Logger 中间件
router.Use(Logger())
// 定义路由和处理函数,这里使用匿名函数
router.GET("/hello", func(ctx *gin.Context) {
// 从请求体中查询 name 参数
name := ctx.Query("name")
fmt.Printf("请求处理...接收到参数:%s \n", name)
ctx.String(http.StatusOK, "Hello Gin! %s", name)
})
router.POST("/add", func(ctx *gin.Context) {
var requestData struct {
Name string "json:name binding:required"
}
// 使用 ShouldBindJSON 方法将请求体中的 JSON 数据绑定到结构体中
if err := ctx.ShouldBindJSON(&requestData); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error:": err.Error()})
return
}
// 从结构体中获取参数
name := requestData.Name
if len(name) == 0 {
fmt.Println("获取name参数失败!")
ctx.String(http.StatusBadRequest, "add fail!")
} else {
fmt.Printf("添加操作:%+v 完成\n", name)
ctx.String(http.StatusOK, "%s add successful!", name)
}
})
// 启动 HTTP 服务器
router.Run("127.0.0.1:8080")
}
通过 Postman 请求测试:
从上面的截图中可以看处,每次请求都调用到了中间件 Logger()。
上面是全局中间件,所有请求都会进入中间件,但是有时候并非所有请求都需要中间件处理,这时候就需要某几个特殊的 API 接口实现中间件即可。
package main
import (
"errors"
"fmt"
"log"
"net/http"
"github.com/gin-gonic/gin"
)
// 定义局部中间件函数,检查用户权限
func checkAuth() gin.HandlerFunc {
return func(ctx *gin.Context) {
token := ctx.Query("token")
if len(token) == 0 {
ctx.Error(errors.New("无权访问"))
ctx.JSON(http.StatusForbidden, gin.H{"code": 403, "message": "无权访问"})
} else {
log.Printf("请求方法:%s 请求路径:%s 正常访问", ctx.Request.Method, ctx.Request.URL.Path)
}
}
}
func main() {
// 创建一个 Gin 引擎
router := gin.Default()
router.GET("/list", checkAuth(), func(ctx *gin.Context) {
errors := ctx.Errors
if errors != nil {
ctx.Errors = nil
return
}
data := struct {
// 注意,这里定义字段一定要大写,要不然则不能导出,返回空
Id interface{} `json:"id"`
Name string `json:"name"`
}{
Id: 1,
Name: "phen",
}
fmt.Println("data: ", data)
ctx.JSON(http.StatusOK, gin.H{"code": 200, "message": "请求成功", "data": data})
})
// 启动 HTTP 服务器
router.Run("127.0.0.1:8080")
}
通过浏览器访问测试:
访问成功:
无权访问:
从两张截图中可以看出,有 token 则访问成功,没 token 则人访问失败!
注意、注意、注意:
这里遇到一个问题,第一次测试是我的代码中没有 ctx.Errors = nil
这句代码,就发现如下情况:
因为 Gin 框架上下文在一个请求中是共享的,所以在同一个请求的不同处理函数中,上下文中的数据和异常信息是可以共享的。
这种设计有助于在请求的不同处理阶段中传递数据和异常信息,但也需要小心处理,以确保不会导致意外的结果。如果你想要在每次请求中使用一个干净的上下文,可以在每个请求处理函数的开头重新初始化上下文,或者在处理函数之间隔离上下文。
如果你不希望在不同请求之间共享异常信息,你可以在每次请求开始时,将上下文清空或初始化,以确保上下文不会受到之前请求的影响。
所以我加入了这句代码 ctx.Errors = nil
,每次访问过之后清楚调异常信息,则每次访问的时候,带 token 则访问成功,不带则无权访问,这样的处理对于新手的我来说应该不是很合理,后面找大佬咨询,或者哪位大佬看到了,评论区指导一下
。
有点:
缺点:
Gin 框架中提供了一种简便的方式来处理函数中发生的异常,确保错误能够捕获并且正确的返回给前端。
在下面的案例中,定义了一个 failFunc() 函数,此函数的作用就是返回一个异常,当在 Get() 方法中获取到的 err 时,则用
ctx.Error(err)
将错误信息存储在 Gin 上下文中。这样,Gin 框架会在请求处理完毕后,检查是否有存储的异常信息,如果有则把错误信息返回给前端。
package main
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
)
func failFunc() error {
return errors.New("运行异常")
}
func main() {
// 创建一个 Gin 引擎
router := gin.Default()
// 创建一个 Get 请求
router.GET("/test/:id", func(ctx *gin.Context) {
err := failFunc()
if err != nil {
// 将错误信息存储在 Gin 上下文中
ctx.Error(err)
ctx.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "服务器异常"})
return
}
ctx.JSON(http.StatusOK, gin.H{"code": 200, "message": "请求成功"})
})
// 启动服务
router.Run("127.0.0.1:8080")
}
通过 postman 调用测试:
请求:http://127.0.01:8080/test/10?name=phen
响应:
{
"code": 500,
"message": "服务器异常"
}
不过我发现,如果在出现异常是,也就是 err 不为空是,里面的 return
不要,则后面所有的 ctx.JSON、ctx.String等,都会成功放回,所以请求结果会如下:
请求:http://127.0.01:8080/test/10?name=phen
响应:
{
"code": 500,
"message": "服务器异常"
}{
"code": 200,
"message": "请求成功"
}请求结束
这一点不会像 Spring框架中,response 过之后,如果再 response 则会报错。
总之,Gin 框架通过在 Gin 上下文中存储错误信息的方式,提供了方法处理错误的机制,使得处理函数中的错误被捕获并且能够返回给前端。
这样有助于用户体验和调试信息。
而也可以通过
ctx.Errors
获取到所有运行中的异常信息
fmt.Println("运行时的所有异常:", ctx.Errors)
只要在 return 前通过 Errors() 方法获取存储在 Gin 中的异常信息,就可以拿到所有的运行过程中的异常,以便业务逻辑的判断和处理。
什么是微框架(Gin),Gin 框架是一个轻量级的,高性能的 Web 框架,专门为构架快速的 Web 应用和 API 而设计。它是基于 Go 语言的标准库开发,提供简单易用的 API 和其他更多的功能,开发者可以快速的构建出 Web 应用并且部署。
通过 go get github.com/gin-gonic/gin 来安装 Gin 框架到开发者环境,通过 框架提供的 路由和处理函数来快速开发 API,接受参数通过函数处理业务逻辑,然后通过 Gin 框架提供的 JSON、String、HTML等方法返回给前端不同类型数据。
通过中间件来处理公共的业务逻辑,比如认证授权、日志记录等。
也有异常处理机制,Gin 通过上下文存储异常信息,然后把异常信息返回给前端来实现异常的处理机制,提高用户体验和调试信息。
最后,Gin 框架总算是有些入门了,后面继续学习框架其他内容,然后博客分享。
现阶段还是对 Go 语言的学习阶段,想必有一些地方考虑的不全面,本文示例全部是亲自手敲代码并且执行通过。
如有问题,还请指教。
评论去告诉我哦!!!一起学习一起进步!!!