1. Gin框架概述
Gin 是一个用 Go (Golang) 编写的 web 框架。它具有运行速度快,分组的路由器,良好的异常捕获和错误处理,非常好的支持中间件和 json 等特性。
2. Gin框架安装
在终端输入以下命令下载Gin
go get github.com/gin-gonic/gin
2.1 编辑 main.go 文件
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
// 默认端口 8080
r.Run()
}
2.2 启动
go run main.go
可以看到,我们在main方法中声明了 path 为 /ping 的接口,我们使用Postman上请求下
3. 配置路由
在这里可以做很多事情,比如声明接口是否需要授权、对API进行版本控制等
为了更好的熟悉,我们先定义个需求:
- 定义两个接口,分别为获取用户列表、添加用户
- 对添加用户接口进行授权验证
3.1 鉴权
3.1.1 准备工作
3.1.1.1 常量类
- 创建包 constant/server
package server
// 服务相关常量
const (
// 服务默认端口
PORT = ":8080"
// 默认token
SECRET = "4b58856e-c396-4427-656f-4cf053f4bd67"
// token名称
TOKEN_NAME = "token"
)
3.1.1.2 工具类
- 创建包 util/security
package security
import (
"net/http"
"MyGo/constant/server"
"github.com/gin-gonic/gin"
)
// 创建签名
func CreateSign(request *gin.Context) {
request.Header(server.TOKEN_NAME, server.SECRET)
}
// 验证签名
func VerifySign(request *gin.Context) {
requestToken := request.GetHeader(server.TOKEN_NAME)
if requestToken == "" && requestToken != server.SECRET {
request.JSON(http.StatusInternalServerError, "Token verify fail")
request.Abort()
return
}
}
3.1.1.3 服务类
- 创建包 service/login
package login
import (
"net/http"
"MyGo/util/security"
"github.com/gin-gonic/gin"
)
// 登录获取Token
func Login(request *gin.Context) {
security.CreateSign(request)
request.JSON(http.StatusOK, "请求成功")
}
- 创建包 service/user
package user
import (
"net/http"
userMapper "MyGo/mapper/user"
"MyGo/model"
"github.com/gin-gonic/gin"
)
// 查询用户列表
func QueryAll(request *gin.Context) {
users := userMapper.QueryUsers()
request.JSON(http.StatusOK, users)
}
// 添加用户
func AddUser(request *gin.Context) {
var user model.User
err := request.ShouldBind(&user)
if err != nil {
request.JSON(http.StatusInternalServerError, "参数解析失败")
}
userMapper.AddUser(&user)
request.JSON(http.StatusOK, "请求成功")
}
3.1.1.4 配置路由
- 创建包 router
package router
import (
"MyGo/service/login"
userService "MyGo/service/user"
"MyGo/util/security"
"github.com/gin-gonic/gin"
)
func InitRouter(r *gin.Engine) {
// 定义开放API分组
GroupOpen := r.Group("/login")
{
GroupOpen.GET("/login", login.Login)
}
// 定义 v1 API分组
GroupV1 := r.Group("/v1")
{
GroupV1.GET("/user/list", userService.QueryAll)
}
// 定义 v2 API分组,该分组使用 security 工具类中 VerifySign 函数校验Token
GroupV2 := r.Group("/v2", security.VerifySign)
{
GroupV2.POST("/user/add", userService.AddUser)
}
}
3.1.1.5 编辑main.go文件
package main
import (
"MyGo/constant/server"
"MyGo/router"
"github.com/gin-gonic/gin"
)
func main() {
engine := gin.Default()
// 注册router配置
router.InitRouter(engine)
// 默认端口 8080
engine.Run(server.PORT)
}
3.1.2 测试鉴权
3.1.2.1 测试 v1 分组API
使用Postman请求 /v1/user/list接口
可以看到,该接口没有token也可以请求成功
3.1.2.1 测试 v2 分组API
使用Postman请求 /v2/user/add 接口
该接口没有token,请求失败了,提示 “Token verify fail” 错误,证明请求进入了 VerifySign 函数内
3.1.2.3 获取token再次访问 v2分组API
获取token
将token写入header再次请求add方法
请求成功了
数据也写进去了
3.2 日志
Gin 框架的日志默认只会在控制台输出,咱们利用 Logrus
封装一个中间件,将日志记录到文件中。
3.2.1 安装依赖包
go get github.com/sirupsen/logrus
go get github.com/lestrrat-go/file-rotatelogs
go get github.com/rifflock/lfshook
3.2.2 添加日志配置
log:
path: /newCode/go/MyGo/logs
name: system.log
自动创建目录文件暂时还不了解,所以先手动在该目录下创建 system.log 文件
3.2.3 创建日志工具类
- 创建包 util/log
package log
import (
"MyGo/util/config"
"fmt"
"github.com/gin-gonic/gin"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
"os"
"path"
"time"
)
func LoggerWrite() gin.HandlerFunc {
config := config.GetConfig("./config/", "config_dev", config.YAML)
logFilePath := config.GetString("log.path")
logFileName := config.GetString("log.name")
// 日志文件
fileName := path.Join(logFilePath, logFileName)
// 写入文件
src, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
if err != nil {
fmt.Println("err", err)
}
// 实例化
logger := logrus.New()
// 设置输出
logger.Out = src
// 设置日志级别
logger.SetLevel(logrus.DebugLevel)
// 设置 rotatelogs
logWriter, err := rotatelogs.New(
// 分割后的文件名称
fileName+".%Y%m%d.log",
// 生成软链,指向最新日志文件
rotatelogs.WithLinkName(fileName),
// 设置最大保存时间(7天)
rotatelogs.WithMaxAge(7*24*time.Hour),
// 设置日志切割时间间隔(1天)
rotatelogs.WithRotationTime(24*time.Hour),
)
writeMap := lfshook.WriterMap{
logrus.InfoLevel: logWriter,
logrus.FatalLevel: logWriter,
logrus.DebugLevel: logWriter,
logrus.WarnLevel: logWriter,
logrus.ErrorLevel: logWriter,
logrus.PanicLevel: logWriter,
}
lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{
TimestampFormat: "2006-01-02 15:04:05",
})
// 新增钩子
logger.AddHook(lfHook)
return func(c *gin.Context) {
// 开始时间
startTime := time.Now()
// 处理请求
c.Next()
// 结束时间
endTime := time.Now()
// 执行时间
latencyTime := endTime.Sub(startTime)
// 请求方式
reqMethod := c.Request.Method
// 请求路由
reqUri := c.Request.RequestURI
// 状态码
statusCode := c.Writer.Status()
// 请求IP
clientIP := c.ClientIP()
// 日志格式
logger.WithFields(logrus.Fields{
"status_code": statusCode,
"latency_time": latencyTime,
"client_ip": clientIP,
"req_method": reqMethod,
"req_uri": reqUri,
}).Info()
}
}
3.2.4 开启日志
package main
import (
"MyGo/constant/server"
"MyGo/router"
"MyGo/util/log"
"github.com/gin-gonic/gin"
)
func main() {
// 默认为 debug 模式,设置为发布模式
gin.SetMode(gin.ReleaseMode)
engine := gin.Default()
// 配置日志
engine.Use(log.LoggerWrite())
// 配置路由
router.InitRouter(engine)
// 默认端口 8080
engine.Run(server.PORT)
}
3.2.5 请求接口,校验日志是否开启
日志信息成功写入文件,但是可以发现是有个failed的
failed to rotate: failed to create new symlink: symlink \newCode\go\MyGo\logs\system.log.20230214.log /newCode/go/MyGo/logs/system.log.20230214.log_symlink: A required privilege is not held by the client.
使用的为 win10系统,需要开启开发者模式