Gin
大约 3 分钟
Gin
介绍
- 2022/9/23
安装
- go get -u github.com/gin-gonic/gin
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",
})
})
r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}
优雅停机
var R = gin.New()
// Init 初始化
func Init() {
//WxText()
//其他初始化,必须在此之上
var port = ":" + YmlConfig["port"].(string)
var url = "http://" + util.GetTargetMaskIp("24")[0] + port
log.Println("Gin: 初始化,地址: " + url)
//下面都是优雅停机方式
srv := &http.Server{
Addr: port,
Handler: R,
}
go func() {
// 服务连接
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// 等待中断信号以优雅地关闭服务器(设置 10 秒的超时时间)
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
<-quit
log.Println("关闭服务器...")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("服务器关闭:", err)
}
log.Println("服务器退出")
}
中间件
异常处理
package middleware
import (
"api/entity"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"runtime/debug"
)
// Recover 全局异常处理器
func Recover(c *gin.Context) {
defer func() {
if r := recover(); r != nil {
debug.PrintStack() //打印错误堆栈信息
c.JSON(http.StatusOK, entity.Fail(errorToString(r)))
c.Abort() //终止后续接口调用,不加的话,还会继续接口后续代码
}
}()
c.Next()
}
// NewServiceError 使用关键字 panic 抛出异常,否则无法识别到
func NewServiceError(text string) error {
return &ServiceError{text}
}
type ServiceError struct {
s string
}
func (e *ServiceError) Error() string {
return e.s
}
// recover错误,转string
func errorToString(r interface{}) string {
switch v := r.(type) {
case *ServiceError:
return v.Error()
default:
return fmt.Sprint(v)
}
}
cors跨域
package config
import (
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"log"
"time"
)
func init() {
initCors()
}
func initCors() {
log.Println("跨域处理器!")
R.Use(Cors())
}
// Cors 跨域配置
func Cors() gin.HandlerFunc {
handlerFunc := cors.New(cors.Config{
AllowMethods: []string{"*"},
AllowHeaders: []string{"Authentication"}, //此处设置非默认之外的请求头(自定义请求头),否则会出现跨域问题,必须制定名称
AllowAllOrigins: true,
AllowCredentials: true,
MaxAge: 24 * time.Hour,
})
return handlerFunc
}
缓存redis
package config
import (
"gin-gin/entity"
"github.com/gin-gonic/gin"
"go-util/util"
"log"
"net/http"
"time"
)
var CachePath []string
// 需要配合Redis进行使用!
func init() {
//initCache()
}
func initCache() {
log.Println("缓存处理器!")
R.Use(Cache())
}
// Cache Redis缓存中间件
func Cache() gin.HandlerFunc {
return func(c *gin.Context) {
path := util.GinUrlPath()
for _, i := range CachePath {
//需要缓存该返回结果!
if i == path {
cacheStr := RedisGetString(path)
log.Println("触发缓存:" + path)
if cacheStr == "" {
c.Next()
return
}
var r entity.R
util.JsonToObj(cacheStr, &r)
c.JSON(http.StatusOK, r)
c.Abort() //终止访问
break
}
}
}
}
// CacheData 添加需要缓存的路径,默认30秒
func CacheData(k, v string) {
isExistPath := false
for _, s := range CachePath {
if s == k {
log.Println("触发缓存:" + k)
isExistPath = true
break
}
isExistPath = false
}
if !isExistPath {
CachePath = append(CachePath, k)
}
log.Println(CachePath)
RedisSetExpiration(k, v, 30*time.Second)
}
// CachePathUrlData 根据路径进行缓存 /file/list?xx=aa => /file/list
func CachePathUrlData(v string) {
CacheData(util.GinUrlPath(), v)
}
// GetParamsUrlPahData 根据路径+参数进行缓存 /file/list?xx=aa => /file/list?xx=aa
func GetParamsUrlPahData(v string) {
CacheData(util.GinParamsUrlPah(), v)
}
认证处理器
package config
import (
"context"
"gin-gin/common"
"gin-gin/entity"
"github.com/gin-gonic/gin"
"go-util/util"
"log"
"net/http"
"strconv"
"strings"
)
// Authorization 请求头key
var Authorization = "Authorization"
// 排除路径
var excludePath []interface{}
// go的加载特性,是从字母顺序进行加载文件的!
func init() {
//initAuth()
}
func initAuth() {
log.Println("认证处理器!")
excludePath = YmlConfig["excludePath"].([]interface{})
R.Use(Auth())
}
// Auth 认证拦截器
func Auth() gin.HandlerFunc {
return func(c *gin.Context) {
//保存到上下文中
util.GinSetContext(c)
path := util.GinUrlPath()
//log.Println(path)
//排除路径处理
for _, item := range excludePath {
// 排除路径例如: /test/** , 排除匹配 /test/ 的所有拦截
var ePath = item.(string)
//检查是否存在**号
if strings.HasSuffix(ePath, "**") || path == ePath {
var prefix = strings.ReplaceAll(ePath, "*", "")
if strings.HasPrefix(path, prefix) {
return
}
}
}
//登入认证
query := c.GetHeader(Authorization)
if query == "" {
log.Println("账号没有登入!")
c.JSON(http.StatusOK, entity.Fail("没有登入!"))
c.Abort() //终止访问
return
}
//权限认证-还可以做其他的认证!
var user entity.User
userId, _ := strconv.ParseInt(util.GetTokenParse(query), 10, 8)
common.Db.Where(entity.User{Id: userId}).First(&user)
if user.Deleted == 1 {
c.JSON(http.StatusOK, entity.Fail("用户已禁用!"))
c.Abort() //终止访问
return
}
Ctx = context.WithValue(Ctx, util.UserIdKey, userId)
c.Next()
//拦截后
}
}
工具类
对象封装
package entity
type R struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
// OkData 成功的数据返回
func OkData(data interface{}) R {
return R{Code: 0, Data: data}
}
// OkMsg 成功的消息返回
func OkMsg(msg string) R {
return R{Code: 0, Msg: msg}
}
// Fail 失败的消息返回
func Fail(msg string) R {
return R{Code: -1, Msg: msg}
}