Gin学习笔记

时间:2025-03-10 07:00:44

RESTful API

以前写网站

  • get /user

  • post /create_user

  • post /update_user

  • post /delete_user

RESTful API

  • get /user 获取

  • post /user 新建

  • put /user 更新

  • patch /user 更新部分

  • delete /user 删除

  • REST与技术无关,代表的是一种软件架构风格只要API程序遵循了REST风格,那就可以称其为RESTful API

  • REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作

    • get

    • post

    • put

    • patch

    • delete

  • Gin框架支持开发RESTful API的开发

  • package main
    
    import (
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    )
    
    func main() {
    	// 创建一个服务
    	ginServer := gin.Default()
    	// 访问地址,处理请求 Request Response
    	ginServer.GET("/hello", func(context *gin.Context) {
    		context.JSON(200, gin.H{"msg": "hello,world"})
    	})
    	ginServer.POST("/user", func(ctx *gin.Context) {
    		ctx.JSON(http.StatusOK, gin.H{"msg": "post请求"})
    	})
    	ginServer.PUT("/user")
    	ginServer.DELETE("/user")
    
    	// 服务器端口
    	err := ginServer.Run(":8082")
        // http.ListenAndServe(":8082", ginServer)
    	if err != nil {
    		return
    	}
    }

第一个Gin示例

// cmd
go get -u github.com/gin-gonic/gin
package main

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

func main() {
    // 	gin.SetMode(gin.ReleaseMode) // 切换到生产模式
	// 创建一个服务
	ginServer := gin.Default()
	// 访问地址,处理请求 Request Response
	ginServer.GET("/hello", func(context *gin.Context) {
		context.JSON(200, gin.H{"msg": "hello,world"})
        // http.StatusOK就是请求已经成功的200的状态码
	})

	// 服务器端口
    err := ginServer.Run(":8082")
    if err != nil {
		return
	}
}
  • 想要更改左上角的图标

    • package main
      
      import (
      	"github.com/gin-gonic/gin"
          "github.com/thinkerou/favicon" // go get
      )
      
      func main() {
          // 	gin.SetMode(gin.ReleaseMode) // 切换到生产模式
      	// 创建一个服务
      	ginServer := gin.Default()
          ginServer.Use(favicon.New("./favicon.ico"))
      	// 访问地址,处理请求 Request Response
      	ginServer.GET("/hello", func(context *gin.Context) {
      		context.JSON(200, gin.H{"msg": "hello,world"})
      	})
      
      	// 服务器端口
      	ginServer.Run(":8082")
      }
  • IP变为内网IP

    • package main
      
      import (
      	"net/http"
      
      	"github.com/gin-gonic/gin"
      )
      
      func main() {
          // 	gin.SetMode(gin.ReleaseMode) // 切换到生产模式
      	router := gin.Default()
      	router.GET("/index", func(ctx *gin.Context) {
      		ctx.String(200, "Hello")
      	})
      	// 启动监听,gin会把web服务运行在本机的0.0.0.0:8080端口上
      	router.Run("0.0.0.0:8082")
      	// 用原生http服务的方式, router.Run本质就是http.ListenAndServe的进一步封装
      	http.ListenAndServe(":8082", router)
      }

加载静态页面

  • package main
    
    import (
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    )
    
    func main() {
        // 	gin.SetMode(gin.ReleaseMode) // 切换到生产模式
    	// 创建一个服务
    	ginServer := gin.Default()
    	// ginServer.Use(favicon.New("./favicon.ico"))
    
    	// 加载静态页面
    	ginServer.LoadHTMLGlob("templates/*")
    	// ginServer.LoadHTMLFiles("templates/index.html")
    	// LoadHTMLGlob是全局加载Files是指定文件
    
    	// 响应一个页面给前端
    	ginServer.GET("/index", func(context *gin.Context) {
    		// context.JSON() json数据
    		context.HTML(http.StatusOK, "index.html", gin.H{
    			"msg": "这是go后台传入的数据",
    		})
    	})
    
    	// 服务器端口
    	err := ginServer.Run(":8082")
    	if err != nil {
    		return
    	}
    }
  • 新建一个文件夹templates,在其下面创建index.html文件

  • <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>一个GoWeb页面</title>
    </head>
    <body>
        <h1>感谢大家支持江小年的博客</h1>
        获取后端的数据为:
        {{.msg}}
    </body>
    </html>

加载资源包

  • 创建static文件夹

    • 在其中创建css文件夹

      • style.css

    • js文件夹

      • common.js

  • package main
    
    import (
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    )
    
    func main() {
        // 	gin.SetMode(gin.ReleaseMode) // 切换到生产模式
    	// 创建一个服务
    	ginServer := gin.Default()
    	// ginServer.Use(favicon.New("./favicon.ico"))
    
    	// 加载静态页面
    	ginServer.LoadHTMLGlob("templates/*")
    	// ginServer.LoadHTMLFiles("templates/index.html")
    	// LoadHTMLGlob是全局加载Files是指定文件
    
    	//加载资源文件
    	ginServer.Static("/static", "./static")
    
    	// 响应一个页面给前端
    	ginServer.GET("/index", func(context *gin.Context) {
    		// context.JSON() json数据
    		context.HTML(http.StatusOK, "index.html", gin.H{
    			"msg": "这是go后台传入的数据",
                "title": "你猜"
    		})
    	})
    
    	// 服务器端口
    	err := ginServer.Run(":8082")
    	if err != nil {
    		return
    	}
    }
  • <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>一个GoWeb页面</title>
        <link rel="stylesheet" href="/static/css/style.css">
        <script src="/static/js/common.js"></script>
    </head>
    <body>
        <h1>感谢大家支持江小年的博客</h1>
        获取后端的数据为:
        {{.msg}}
        title: 
        {{.title}}
    </body>
    </html>
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	gin.SetMode(gin.ReleaseMode)

	ginServer := gin.Default()

	ginServer.LoadHTMLFiles("static/index.html")
	ginServer.Static("/css", "static/css")
	ginServer.Static("/font", "static/font")

	ginServer.GET("index", func(ctx *gin.Context) {
		ctx.HTML(http.StatusOK, "index.html", gin.H{
			"msg": "jiangxiaonian",
		})
	})

	err := ginServer.Run(":8080")
	if err != nil {
		return
	}
}

获取参数

获取请求参数

  • // usl?userid=XXX&username=jaingxionian
    // /user/info/1/jiangxiaonian
  • // main.go
    package main
    
    import (
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    )
    
    func main() {
    	// 创建一个服务
    	ginServer := gin.Default()
    	// ginServer.Use(favicon.New("./favicon.ico"))
    
    	// 加载静态页面
    	ginServer.LoadHTMLGlob("templates/*")
    	// ginServer.LoadHTMLFiles("templates/index.html")
    	// LoadHTMLGlob是全局加载Files是指定文件
    
    	//加载资源文件
    	ginServer.Static("/static", "./static")
    
    	// 响应一个页面给前端
    	ginServer.GET("/index", func(context *gin.Context) {
    		// context.JSON() json数据
    		context.HTML(http.StatusOK, "index.html", gin.H{
    			"msg": "这是go后台传入的数据",
    		})
    	})
    
    	// usl?userid=XXX&username=jaingxionian
    	ginServer.GET("/user/info", func(context *gin.Context) {
    		userid := context.Query("userid")
            username := context.Query("username")
    
            // fmt.Println(c.QueryArray("userid")) // 拿到多个相同的查询参数
            // fmt.Println(c.DefaultQuery("userid", 0))
            
    		context.JSON(http.StatusOK, gin.H{
    			"userid":   userid,
    			"username": username,
    		})
    	})
    
    	// /user/info/1/jiangxiaonian
    	// 只要:后名字正确就能匹配上
    	ginServer.GET("/user/info/:userid/:username", func(context *gin.Context) {
    		userid := context.Param("userid")
    		username := context.Param("username")
    		context.JSON(http.StatusOK, gin.H{
    			"userid":   userid,
    			"username": username,
    		})
    	})
    
    	// 服务器端口
    	err := ginServer.Run(":8082")
    	if err != nil {
    		return
    	}
    }
    // 运行后访问 http://localhost:8082/user/info?userid=1&username=jaingxiaonain

获取前端给后端传递的json(序列化)参数

// gin 优化过的BindJSON
package controllers

import "github.com/gin-gonic/gin"

type OrderController struct{}

func (o OrderController) GetList(c *gin.Context) {
	m := make(map[string]interface{})
	err := c.BindJSON(&m)
	if err == nil {
		c.JSON(http.StatusOK, m)
		return
	}
	c.JSON(4001, gin.H{"err": err})
}
// 结构体
package controllers

import "github.com/gin-gonic/gin"

type OrderController struct{}

type Search struct {
	Name string `json:"name"`
	Cid  int    `json:"cid"`
}

func (o OrderController) GetList(c *gin.Context) {
	search := &Search{}
	err := c.BindJSON(&search)
	if err == nil {
		ReturnSuccess(c, 0, search.Name, search.Cid, 1)
		return
	}
	ReturnErrer(c, 4001, gin.H{"err": err})
}

// 未优化的*gin.Context.GetRawData()  json.Unmarshal()
// 前段给后端传JSON
ginServer.POST("/json", func(context *gin.Context) {
	// GetRawData() 从请求体(request.body)里获取对象
	b, _ := context.GetRawData()
	var m map[string]interface{}
	// 包装为json数据 []byte
	_ = json.Unmarshal(b, &m)
	context.JSON(http.StatusOK, m)
})

获取表单中的参数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>一个GoWeb页面</title>
    <link rel="stylesheet" href="/static/css/style.css">
    <script src="/static/js/common.js"></script>
</head>
<body>
    <h1>感谢大家支持江小年的博客</h1>
    <form action="/user/add" method="post">
        <p>username: <input type="text" name="username"></p>
        <p>password: <input type="password" name="password"></p>
        <button type="submit"> 提交 </button>
    </form>
</body>
</html>
// .DefaultPostForm()  .PostForm()
package main

import (
	"encoding/json"
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	// 创建一个服务
	ginServer := gin.Default()
	// ginServer.Use(favicon.New("./favicon.ico"))
	// 加载静态页面
	ginServer.LoadHTMLGlob("templates/*")
	// ginServer.LoadHTMLFiles("templates/index.html")
	// LoadHTMLGlob是全局加载Files是指定文件
	//加载资源文件
	ginServer.Static("/static", "./static")
	// 响应一个页面给前端
	ginServer.GET("/index", func(context *gin.Context) {
		// context.JSON() json数据
		context.HTML(http.StatusOK, "index.html", gin.H{
			"msg": "这是go后台传入的数据",
		})
	})

	// 表单
	ginServer.POST("/user/add", func(context *gin.Context) {
		username := context.PostForm("username")
		password := context.PostForm("password")
        // password := context.DefaultPostForm("password", 12345)第二个参数为默认值

		// 加判断逻辑代码

		context.JSON(http.StatusOK, gin.H{
			"msg":      "ok",
			"username": username,
			"password": password,
		})
	})

	// 服务器端口
	err := ginServer.Run(":8082")
	if err != nil {
		return
	}
}

路由

HTTP重定向

  • package main
    
    import (
    	"encoding/json"
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    )
    
    func main() {
    	// 创建一个服务
    	ginServer := gin.Default()
    	// 响应一个页面给前端
    	ginServer.GET("/index", func(context *gin.Context) {
    		// context.JSON() json数据
    		context.HTML(http.StatusOK, "index.html", gin.H{
    			"msg": "这是go后台传入的数据",
    		})
    	})
    
    	// 路由
    	ginServer.GET("/test", func(context *gin.Context) {
    		// 重定向    StatusMovedPermanently 301
    		context.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
    	})
    
    	// 服务器端口
    	err := ginServer.Run(":8082")
    	if err != nil {
    		return
    	}
    }

路由重定向

路由重定向,使用HandleContext

package main

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

func main() {
	r := gin.Default()
	r.GET("/test", func(c *gin.Context) {
		// 指定重定向的URL
		c.Request.URL.Path = "/test2"
		r.HandleContext(c)
	})
	r.GET("/test2", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"hello": "world"})
	})
	// Listen and serve on 0.0.0.0:8080
	err := r.Run(":8080")
	if err != nil {
		return
	}
}

404 NoRoute()

  • <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>404</title>
    </head>
    <body>
        
        <h1>江小年的404页面</h1>
        
    </body>
    </html>
  • package main
    
    import (
    	"encoding/json"
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    )
    
    func main() {
    	// 创建一个服务
    	ginServer := gin.Default()
    	// 路由
    	ginServer.GET("/test", func(context *gin.Context) {
    		// 重定向    StatusMovedPermanently 301
    		context.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
    	})
    
    	// 404 NoRoute
    	ginServer.NoRoute(func(context *gin.Context) {
    		context.HTML(http.StatusNotFound, "404.html", nil)
    	})
    
    	// 服务器端口
    	err := ginServer.Run(":8082")
    	if err != nil {
    		return
    	}
    }

路由组

package main

import (
	"encoding/json"
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	// 创建一个服务
	ginServer := gin.Default()
	// ginServer.Use(favicon.New("./favicon.ico"))

	// 加载静态页面
	ginServer.LoadHTMLGlob("templates/*")
	// ginServer.LoadHTMLFiles("templates/index.html")
	// LoadHTMLGlob是全局加载Files是指定文件

	//加载资源文件
	ginServer.Static("/static", "./static")

	// 响应一个页面给前端
	ginServer.GET("/index", func(context *gin.Context) {
		// context.JSON() json数据
		context.HTML(http.StatusOK, "index.html", gin.H{
			"msg": "这是go后台传入的数据",
		})
	})

	// usl?userid=XXX&username=jaingxionian
	ginServer.GET("/user/info", func(context *gin.Context) {
		userid := context.Query("userid")
		username := context.Query("username")
		context.JSON(http.StatusOK, gin.H{
			"userid":   userid,
			"username": username,
		})
	})

	// /user/info/1/jiangxiaonian
	// 只要:后名字正确就能匹配上
	ginServer.GET("/user/info/:userid/:username", func(context *gin.Context) {
		userid := context.Param("userid")
		username := context.Param("username")
		context.JSON(http.StatusOK, gin.H{
			"userid":   userid,
			"username": username,
		})
	})

	// 前段给后端传JSON
	ginServer.POST("/json", func(context *gin.Context) {
		// GetRawData() 从请求体(request.body)里获取对象
		b, _ := context.GetRawData()
		var m map[string]interface{}
		// 包装为json数据 []byte
		_ = json.Unmarshal(b, &m)
		context.JSON(http.StatusOK, m)
	})

	// 表单
	ginServer.POST("/user/add", func(context *gin.Context) {
		username := context.PostForm("username")
		password := context.PostForm("password")

		// 加判断逻辑代码

		context.JSON(http.StatusOK, gin.H{
			"msg":      "ok",
			"username": username,
			"password": password,
		})
	})

	// 路由
	ginServer.GET("/test", func(context *gin.Context) {
		// 重定向    StatusMovedPermanently 301
		context.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
	})

	// 404 NoRoute
	ginServer.NoRoute(func(context *gin.Context) {
		context.HTML(http.StatusNotFound, "404.html", nil)
	})

	// 路由组
	userGroup := ginServer.Group("/user")
	{
		userGroup.GET("/add")    // /user/add
		userGroup.GET("/login")  // /user/add
		userGroup.GET("/logout") // /user/add
	}

	orderGroup := ginServer.Group("/order")
	{
		orderGroup.GET("add")
		orderGroup.GET("delte")

	}

	// 服务器端口
	err := ginServer.Run(":8082")
	if err != nil {
		return
	}
}

路由嵌套

shopGroup := r.Group("/shop")
	{
		shopGroup.GET("/index", func(c *gin.Context) {...})
		shopGroup.GET("/cart", func(c *gin.Context) {...})
		shopGroup.POST("/checkout", func(c *gin.Context) {...})
		// 嵌套路由组
		xx := shopGroup.Group("xx")
		xx.GET("/oo", func(c *gin.Context) {...})
    }

中间件(Java中为拦截器)

package main

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
)

// 自定义Go中间件 拦截器
func myHandler() gin.HandlerFunc {
	return func(context *gin.Context) {
		// Set一些值用作全局变量
		// 通过自定义的中间件,设置的值,在后续处理只要调用了这个中间件的都可以拿到这里的参数
		context.Set("usersession", "userid-1")
		context.Next() // 放形
		/* if XXX {
			context.Next() // 放形
		}
		context.Abort() // 阻止
		// 注册未指定就是全局使用
		*/
	}
	/* 46行加入中间件 */
}

func main() {
	// 创建一个服务
	ginServer := gin.Default()
	// ginServer.Use(favicon.New("./favicon.ico"))

	// 加载静态页面
	ginServer.LoadHTMLGlob("templates/*")
	// ginServer.LoadHTMLFiles("templates/index.html")
	// LoadHTMLGlob是全局加载Files是指定文件

	//加载资源文件
	ginServer.Static("/static", "./static")

	// 响应一个页面给前端
	ginServer.GET("/index", func(context *gin.Context) {
		// context.JSON() json数据
		context.HTML(http.StatusOK, "index.html", gin.H{
			"msg": "这是go后台传入的数据",
		})
	})

	// usl?userid=XXX&username=jaingxionian
	ginServer.GET("/user/info", myHandler(), func(context *gin.Context) {

		// 取出中间件中的值
		usersession := context.MustGet("usersession").(string)
		log.Println("========>", usersession) // 前端控制台输出

		userid := context.Query("userid")
		username := context.Query("username")
		context.JSON(http.StatusOK, gin.H{
			"userid":   userid,
			"username": username,
		})
	})

	// /user/info/1/jiangxiaonian
	// 只要:后名字正确就能匹配上
	ginServer.GET("/user/info/:userid/:username", func(context *gin.Context) {
		userid := context.Param("userid")
		username := context.Param("username")
		context.JSON(http.StatusOK, gin.H{
			"userid":   userid,
			"username": username,
		})
	})

	// 前段给后端传JSON
	ginServer.POST("/json", func(context *gin.Context) {
		// GetRawData() 从请求体(request.body)里获取对象
		b, _ := context.GetRawData()
		var m map[string]interface{}
		// 包装为json数据 []byte
		_ = json.Unmarshal(b, &m)
		context.JSON(http.StatusOK, m)
	})

	// 表单
	ginServer.POST("/user/add", func(context *gin.Context) {
		username := context.PostForm("username")
		password := context.PostForm("password")

		// 加判断逻辑代码

		context.JSON(http.StatusOK, gin.H{
			"msg":      "ok",
			"username": username,
			"password": password,
		})
	})

	// 路由
	ginServer.GET("/test", func(context *gin.Context) {
		// 重定向    StatusMovedPermanently 301
		context.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
	})

	// 404 NoRoute
	ginServer.NoRoute(func(context *gin.Context) {
		context.HTML(http.StatusNotFound, "404.html", nil)
	})

	// 路由组
	userGroup := ginServer.Group("/user")
	{
		userGroup.GET("/add")    // /user/add
		userGroup.GET("/login")  // /user/add
		userGroup.GET("/logout") // /user/add
	}

	orderGroup := ginServer.Group("/order")
	{
		orderGroup.GET("add")
		orderGroup.GET("delte")

	}

	// 服务器端口
	err := ginServer.Run(":8082")
	if err != nil {
		return
	}
}

// StatCost 是一个统计耗时请求耗时的中间件
func StatCost() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		c.Set("name", "wxy") // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
		// 调用该请求的剩余处理程序
		c.Next()
		// 不调用该请求的剩余处理程序
		// c.Abort()
		// 计算耗时
		cost := time.Since(start)
		log.Println(cost)
	}
}

多个中间件

func m1(c *gin.Context) {
  fmt.Println("m1 ...in")
}
func m2(c *gin.Context) {
  fmt.Println("m2 ...in")
}

func main() {
  router := gin.Default()

  router.GET("/", m1, func(c *gin.Context) {
    fmt.Println("index ...")
    c.JSON(200, gin.H{"msg": "响应数据"})
  }, m2)

  router.Run(":8080")
}

中间件拦截响应c.Abort()

package main

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

func m1(c *gin.Context) {
    fmt.Println("m1 ...in")
    c.JSON(200, gin.H{"msg": "第一个中间件拦截了"})
    c.Abort()
}
func m2(c *gin.Context) {
    fmt.Println("m2 ...in")
}

func main() {
    router := gin.Default()

    router.GET("/", m1, func(c *gin.Context) {
        fmt.Println("index ...")
        c.JSON(200, gin.H{"msg": "响应数据"})
    }, m2)

    router.Run(":8080")
}

中间件放行c.Next()

package main

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

func m1(c *gin.Context) {
  fmt.Println("m1 ...in")
  c.Next()
  fmt.Println("m1 ...out")
}
func m2(c *gin.Context) {
  fmt.Println("m2 ...in")
  c.Next()
  fmt.Println("m2 ...out")
}

func main() {
  router := gin.Default()

  router.GET("/", m1, func(c *gin.Context) {
    fmt.Println("index ...in")
    c.JSON(200, gin.H{"msg": "响应数据"})
    c.Next()
    fmt.Println("index ...out")
  }, m2)

  router.Run(":8080")
}

文件上传

单个文件上传

文件上传前端页面代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>上传文件示例</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="f1">
    <input type="submit" value="上传">
</form>
</body>
</html>

后端gin框架部分代码:

func main() {
	router := gin.Default()
	// 处理multipart forms提交文件时默认的内存限制是32 MiB
	// 可以通过下面的方式修改
	// router.MaxMultipartMemory = 8 << 20  // 8 MiB
	router.POST("/upload", func(c *gin.Context) {
		// 单个文件
		file, err := c.FormFile("f1")
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"message": err.Error(),
			})
			return
		}

		log.Println(file.Filename)
		dst := fmt.Sprintf("D:/go_file/%s", file.Filename)
		// 上传文件到指定的目录
		c.SaveUploadedFile(file, dst)
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
		})
	})
    router.Run(":8082")
}

copy

file, _ := c.FormFile("file")
log.Println(file.Filename)
// 读取文件中的数据,返回文件对象
fileRead, _ := file.Open()
dst := "./" + file.Filename
// 创建一个文件
out, err := os.Create(dst)
if err != nil {
  fmt.Println(err)
}
defer out.Close()
// 拷贝文件对象到out中
io.Copy(out, fileRead)

读取上传文件

file, _ := c.FormFile("file")
// 读取文件中的数据,返回文件对象
fileRead, _ := file.Open()
data, _ := io.ReadAll(fileRead)
fmt.Println(string(data))

多个文件上传

func main() {
	router := gin.Default()
	// 处理multipart forms提交文件时默认的内存限制是32 MiB
	// 可以通过下面的方式修改
	// router.MaxMultipartMemory = 8 << 20  // 8 MiB
	router.POST("/upload", func(c *gin.Context) {
		// Multipart form
		form, _ := c.MultipartForm()
		files := form.File["file"]

		for index, file := range files {
			log.Println(file.Filename)
			dst := fmt.Sprintf("./upload/%s_%d", file.Filename, index)
			// 上传文件到指定的目录
			c.SaveUploadedFile(file, dst)
		}
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("%d files uploaded!", len(files)),
		})
	})
    router.Run(":8082")
}

文件下载

c.Header("Content-Type", "application/octet-stream")              // 表示是文件流,唤起浏览器下载,一般设置了这个,就要设置文件名
c.Header("Content-Disposition", "attachment; filename="+"牛逼.png") // 用来指定下载下来的文件名
c.Header("Content-Transfer-Encoding", "binary")                   // 表示传输过程中的编码形式,乱码问题可能就是因为它
c.File("uploads/12.png")
/*文件下载浏览器可能会有缓存,这个要注意一下
解决办法就是加查询参数*/

前后端模式下的文件下载

c.Header("fileName", "xxx.png")
c.Header("msg", "文件下载成功")
c.File("uploads/12.png")
async downloadFile(row) {
this.$http({
   method: 'post',
   url: 'file/upload',
   data:postData,
   responseType: "blob"
}).then(res => {
   const _res = res.data
   let blob = new Blob([_res], {
         type: 'application/png'
       });
   let downloadElement = document.createElement("a");
   let href = window.URL.createObjectURL(blob); //创建下载的链接
   downloadElement.href = href;
   downloadElement.download = res.headers["fileName"]; //下载后文件名
   document.body.appendChild(downloadElement);
   downloadElement.click(); //点击下载
   document.body.removeChild(downloadElement); //下载完成移除元素
   window.URL.revokeObjectURL(href); //释放掉blob对象
 })}s

异常捕获

defer func(){
    if err:=recover(); err != nil{
        fmt.Println("捕获异常:", err)
    }
}() // 因为recover只有在发生panic时才会返回一个非nil的值。如果没有panic发生,recover会返回nil

打印日志

// pkg/util/logger.go
package util

import (
	"log"
	"os"
	"path"
	"time"

	"github.com/sirupsen/logrus"
)

var LogrusObj *logrus.Logger

func init() {
	// init() 特殊函数 在包被导入时自动执行
	src, _ := setOutPutFile()
	if LogrusObj != nil {
		LogrusObj.Out = src
		return
	}
	// 实例化
	logger := logrus.New()
	logger.Out = src                   // 设置输出
	logger.SetLevel(logrus.DebugLevel) // 设置日志规则
	logger.SetFormatter(&logrus.TextFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})
	LogrusObj = logger
}

func setOutPutFile() (*os.File, error) {
	now := time.Now()
	logFilePath := ""
	if dir, err := os.Getwd(); err == nil {
		// os.Getwd()获取当前的工作目录
		logFilePath = dir + "/logs/"
	}
	_, err := os.Stat(logFilePath)
	if os.IsNotExist(err) {
		if err = os.MkdirAll(logFilePath, 0777); err != nil {
			log.Println(err.Error())
			return nil, err
		}
	}
	logFileName := now.Format("2006-01-02") + ".log"
	// 日志文件
	fileName := path.Join(logFilePath, logFileName)
	_, err = os.Stat(fileName)
	if os.IsNotExist(err) {
		if err = os.MkdirAll(fileName, 0777); err != nil {
			// os.MkdirAll是用来创建目录的,而不是文件。应该使用os.Create或os.OpenFile来创建文件
			log.Println(err.Error())
			return nil, err
		}
	}
	// 写入文件
	src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
	if err != nil {
		return nil, err
	}
	return src, nil
}
// util.LogrusObj.Infoln(err)

gin自带日志系统

package main

import (
  "github.com/gin-gonic/gin"
  "io"
  "os"
)

func main() {
  // 输出到文件
  f, _ := os.Create("gin.log")
  //gin.DefaultWriter = io.MultiWriter(f)
  // 如果需要同时将日志写入文件和控制台,请使用以下代码。
  gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
  router := gin.Default()
  router.GET("/", func(c *gin.Context) {
    c.JSON(200, gin.H{"msg": "/"})
  })
  router.Run()
}
// 查看路由
router.Routes()  // 它会返回已注册的路由列表

// 环境切换    如果不想看到这些debug日志,那么我们可以改为release模式
gin.SetMode(gin.ReleaseMode)
router := gin.Default()

// 修改log的显示
package main

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

func LoggerWithFormatter(params gin.LogFormatterParams) string {

  return fmt.Sprintf(
    "[ feng ] %s  | %d | \t %s | %s | %s \t  %s\n",
    params.TimeStamp.Format("2006/01/02 - 15:04:05"),
    params.StatusCode,  // 状态码
    params.ClientIP,  // 客户端ip
    params.Latency,  // 请求耗时
    params.Method,  // 请求方法
    params.Path,  // 路径
  )
}
func main() {
  router := gin.New()
  router.Use(gin.LoggerWithFormatter(LoggerWithFormatter))
  router.Run()
}
// --------------------------------
func LoggerWithFormatter(params gin.LogFormatterParams) string {
  return fmt.Sprintf(
    "[ feng ] %s  | %d | \t %s | %s | %s \t  %s\n",
    params.TimeStamp.Format("2006/01/02 - 15:04:05"),
    params.StatusCode,
    params.ClientIP,
    params.Latency,
    params.Method,
    params.Path,
  )
}
func main() {
  router := gin.New()
  router.Use(
    gin.LoggerWithConfig(
      gin.LoggerConfig{Formatter: LoggerWithFormatter},
    ),
  )
  router.Run()
}
// ------------------
func LoggerWithFormatter(params gin.LogFormatterParams) string {
  var statusColor, methodColor, resetColor string
  statusColor = params.StatusCodeColor()
  methodColor = params.MethodColor()
  resetColor = params.ResetColor()
  return fmt.Sprintf(
    "[ feng ] %s  | %s %d  %s | \t %s | %s | %s %-7s %s \t  %s\n",
    params.TimeStamp.Format("2006/01/02 - 15:04:05"),
    statusColor, params.StatusCode, resetColor,
    params.ClientIP,
    params.Latency,
    methodColor, params.Method, resetColor,
    params.Path,
  )
}

*Go跨域

  • 跨域问题

  • 出现跨域问题是浏览器行为,是浏览器认为不安全而进行的拦截

    url的协议、域名、端口三者任一一个与当前页面url不同,就是跨域

  • 如何解决?

    • cors: 在响应头加上对应的响应头即可

    • 代理

cors

package main

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

func Cors() gin.HandlerFunc {
  return func(c *gin.Context) {
    method := c.Request.Method
    if c.Request.Header.Get("origin") != "" {
      c.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名
      c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
      c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
      c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
      c.Header("Access-Control-Allow-Credentials", "true")
    }
    if method == "OPTIONS" {
      c.AbortWithStatus(http.StatusNoContent)
    }
    c.Next()
  }
}

func Index(c *gin.Context) {
  c.JSON(200, gin.H{
    "code": 0,
    "msg":  "成功",
    "data": gin.H{},
  })
  return
}

func main() {
  r := gin.Default()
  r.GET("/api/no_cors", Index)
  r.POST("/api/no_cors", Index)
  r.GET("/api/cors", Cors(), Index)
  r.POST("/api/cors", Cors(), Index)
  r.Run(":8080")
}

代理

这种方案是目前最主流的跨域解决方案,它分为两类,一个是开发环境,一个是生产环境

开发环境解决跨域

vue3为例,vite提供了代理功能

import {fileURLToPath, URL} from 'node:url'

import {defineConfig, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
import type {ImportMetaEnv} from "./env";
// https://vitejs.dev/config/
export default defineConfig(({mode}) => {
    let env: Record<keyof ImportMetaEnv, string> = loadEnv(mode, process.cwd())

    const serverUrl =  env.VITE_SERVER_URL
    const wsUrl = serverUrl.replace("http", "ws")
    return {
        plugins: [
            vue(),
        ],
        envDir: "./",
        resolve: {
            alias: {
                '@': fileURLToPath(new URL('./src', import.meta.url))
            }
        },
        server: {
            host: "0.0.0.0",
            port: 80,
            proxy: {
                "/api": {
                    target: serverUrl,
                    changeOrigin: true,
                }
            }
        }
    }
})

凡是使用代理的情况,axios请求的后端路径就不能写死了

因为一旦写死了,代理就捕获不到了,相当于还是前端直接请求后端接口,肯定会跨域的

生产环境解决跨域

使用nginx的反向代理

server {
    listen       80;
    server_name  blog.fengfengzhidao.com;

    location / {
      try_files $uri $uri/ /index.html;  
      root   /opt/gvb/web/dist;
      index  index.html index.htm;
    }

    location /api/ {
      # rewrite ^/(api/.*) /$1 break;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header REMOTE-HOST $remote_addr;
      proxy_pass http://127.0.0.1:8082/api/;
    }
    location /uploads/ {
      alias /opt/gvb/server/uploads/;
    }

    access_log  /opt/gvb/access.log;
    error_log   /opt/gvb/error.log;
}

*http反向代理(网关)

package main
import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
)
type Proxy struct {}
func (Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	remote, _ := url.Parse("http://127.0.0.1:20023")  // 要转到访问的地址
	reverseProxy := httputil.NewSingleHostReverseProxy(remote)
	reverseProxy.ServeHTTP(w, r)
}
func main() {
	addr := "127.0.0.1:9090"  // 监听地址
	fmt.Println("gateway runserver on %s\n", addr)
	http.ListenAndServe(addr, Proxy{})
}

<!-- -->

Gin

// cmd
go get -u github.com/gin-gonic/gin

项目开始

路由组封装

// main.go
package main

import "godemo/router"

func main() {
	r := router.Router()
	r.Run(":9090")
}
// router/routers.go
package router

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func Router() *gin.Engine {
	r := gin.Default()

	r.GET("/hello", func(ctx *gin.Context) {
		ctx.String(http.StatusOK, "Hello World")
	})

	user := r.Group("/user")
	{
		user.POST("/list", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "user list")
		})
		user.PUT("/add", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "user add")
		})
		user.DELETE("/delete", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "user delete")
		})
	}

	return r
}

封装JS

// controllers/common.go
package controllers

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

type JsonStruct struct {
	Code  int         `json:"code"`
	Msg   interface{} `json:"msg"`
	Data  interface{} `json:"data"`
	Count int64       `json:"count"`
}

func ReturnSuccess(c *gin.Context, code int, msg interface{}, data interface{}, count int64) {
	json := &JsonStruct{
		Code:  code,
		Msg:   msg,
		Data:  data,
		Count: count,
	}
	c.JSON(200, json)
}

func ReturnErrer(c *gin.Context, code int, msg interface{}) {
	json := &JsonStruct{
		Code: code,
		Msg:  msg,
	}
	c.JSON(200, json)
}
// router/routers.go
package router

import (
	"godemo/controllers"
	"net/http"

	"github.com/gin-gonic/gin"
)

func Router() *gin.Engine {
	r := gin.Default()

	r.GET("/hello", func(ctx *gin.Context) {
		ctx.String(http.StatusOK, "Hello World")
	})

	user := r.Group("/user")
	{
		user.GET("/info", controllers.GetUserInfo)

		user.POST("/list", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "user list")
		})
		user.PUT("/add", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "user add")
		})
		user.DELETE("/delete", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "user delete")
		})
	}
	return r
}
// controllers/user.go
package controllers

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

func GetUserInfo(c *gin.Context) {
	ReturnSuccess(c, 0, "success", "user info", 1)
}
  • // router/routers.go
    package router
    
    import (
    	"godemo/controllers"
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    )
    
    func Router() *gin.Engine {
    	r := gin.Default()
    
    	r.GET("/hello", func(ctx *gin.Context) {
    		ctx.String(http.StatusOK, "Hello World")
    	})
    
    	user := r.Group("/user")
    	{
    		user.GET("/info", controllers.GetUserInfo)
    
    		user.POST("/list", controllers.GetList)
    
    		user.PUT("/add", func(ctx *gin.Context) {
    			ctx.String(http.StatusOK, "user add")
    		})
    		user.DELETE("/delete", func(ctx *gin.Context) {
    			ctx.String(http.StatusOK, "user delete")
    		})
    	}
    
    	return r
    }
  • // controllers/user.go
    package controllers
    
    import (
    	"github.com/gin-gonic/gin"
    )
    
    func GetUserInfo(c *gin.Context) {
    	ReturnSuccess(c, 0, "success", "user info", 1)
    }
    
    func GetList(c *gin.Context) {
    	ReturnErrer(c, 4004, "没有相关信息")
    }
  • // controllers/common.go
    package controllers
    
    import (
    	"github.com/gin-gonic/gin"
    )
    
    type JsonStruct struct {
    	Code  int         `json:"code"`
    	Msg   interface{} `json:"msg"`
    	Data  interface{} `json:"data"`
    	Count int64       `json:"count"`
    }
    
    type JsonErrStruct struct {
    	Code int         `json:"code"`
    	Msg  interface{} `json:"msg"`
    }
    
    func ReturnSuccess(c *gin.Context, code int, msg interface{}, data interface{}, count int64) {
    	json := &JsonStruct{
    		Code:  code,
    		Msg:   msg,
    		Data:  data,
    		Count: count,
    	}
    	c.JSON(200, json)
    }
    
    func ReturnErrer(c *gin.Context, code int, msg interface{}) {
    	json := &JsonErrStruct{
    		Code: code,
    		Msg:  msg,
    	}
    	c.JSON(200, json)
    }

结构体优化

  • controllers/user.go中的函数,因为都在一个包里,所以当新建的order.go中出现该函数就会报错,这时就可以用结构体方法进行优化

// controllers/order.go
func GetList(c *gin.Context) {
	ReturnErrer(c, 4004, "没有相关信息")
}

// controllers/user.go
package controllers

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

type UserController struct{}

func (u UserController)GetUserInfo(c *gin.Context) {
	ReturnSuccess(c, 0, "success", "user info", 1)
}

func (u UserController)GetList(c *gin.Context) {
	ReturnErrer(c, 4004, "没有相关信息list")
}
// controllers/order.go
package controllers

import "github.com/gin-gonic/gin"

type OrderContreller struct{}

func (o OrderContreller) GetList(c *gin.Context) {
	ReturnErrer(c, 4004, "没有相关信息")
}
// router/routers.go
package router

import (
	"godemo/controllers"
	"net/http"

	"github.com/gin-gonic/gin"
)

func Router() *gin.Engine {
	r := gin.Default()

	r.GET("/hello", func(ctx *gin.Context) {
		ctx.String(http.StatusOK, "Hello World")
	})

	user := r.Group("/user")
	{
		user.GET("/info", controllers.UserController{}.GetUserInfo)

		user.POST("/list", controllers.UserController{}.GetList)

		user.PUT("/add", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "user add")
		})
		user.DELETE("/delete", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "user delete")
		})
	}

	order := r.Group("/order")
	{
		order.GET("list", controllers.OrderContreller{}.GetList)
	}

	return r
}

获取请求参数

方式一Param

// router/routers.go
package router

import (
	"godemo/controllers"
	"net/http"

	"github.com/gin-gonic/gin"
)

func Router() *gin.Engine {
	r := gin.Default()

	r.GET("/hello", func(ctx *gin.Context) {
		ctx.String(http.StatusOK, "Hello World")
	})

	user := r.Group("/user")
	{
		user.GET("/info/:id/:name", controllers.UserController{}.GetUserInfo)

		user.POST("/list", controllers.UserController{}.GetList)

		user.PUT("/add", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "user add")
		})

		user.DELETE("/delete", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "user delete")
		})
	}

	order := r.Group("/order")
	{
		order.GET("list", controllers.OrderContreller{}.GetList)
	}

	return r
}
// controllers/user.go
package controllers

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

type UserController struct{}

func (u UserController) GetUserInfo(c *gin.Context) {
	id := c.Param("id")
	name := c.Param("name")
	ReturnSuccess(c, 0, name, id, 1)
}

func (u UserController) GetList(c *gin.Context) {
	ReturnErrer(c, 4004, "没有相关信息list")
}

方式二获取POST的参数 PostForm

// controllers/order.go
package controllers

import "github.com/gin-gonic/gin"

type OrderController struct{}

func (o OrderController) GetList(c *gin.Context) {
	cid := c.PostForm("cid")
	name := c.DefaultPostForm("name", "xiaohua")
	ReturnSuccess(c, 0, name, cid, 1)
}

方式三获取JSON参数 BindJSON_ Map&&结构体

// controllers/order.go
package controllers

import "github.com/gin-gonic/gin"

type OrderController struct{}

func (o OrderController) GetList(c *gin.Context) {
	param := make(map[string]interface{})
	err := c.BindJSON(&param)
	if err == nil {
		ReturnSuccess(c, 0, param["name"], param["cid"], 1)
		return
	}
	ReturnErrer(c, 4001, gin.H{"err": err})
}

// controllers/order.go
package controllers

import "github.com/gin-gonic/gin"

type OrderController struct{}

type Search struct {
	Name string `json:"name"`
	Cid  int    `json:"cid"`
}

func (o OrderController) GetList(c *gin.Context) {
	search := &Search{}
	err := c.BindJSON(&search)
	if err == nil {
		ReturnSuccess(c, 0, search.Name, search.Cid, 1)
		return
	}
	ReturnErrer(c, 4001, gin.H{"err": err})
}

异常捕获

// controllers/user.go
package controllers

import (
	"fmt"

	"github.com/gin-gonic/gin"
)

type UserController struct{}

func (u UserController) GetUserInfo(c *gin.Context) {
	id := c.Param("id")
	name := c.Param("name")
	ReturnSuccess(c, 0, name, id, 1)
}

func (u UserController) GetList(c *gin.Context) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("捕获异常:", err)
		}
	}()
    // 因为recover只有在发生panic时才会返回一个非nil的值。如果没有panic发生,recover会返回nil
	num1 := 1
	num2 := 0
	num3 := num1 / num2
	ReturnErrer(c, 4004, num3)
}
// 异常错误输出到日志中

日志收集

// pkg/util/logger.go
package util

import (
	"log"
	"os"
	"path"
	"time"

	"github.com/sirupsen/logrus"
)

var LogrusObj *logrus.Logger

func init() {
	// init() 特殊函数 在包被导入时自动执行
	src, _ := setOutPutFile()
	if LogrusObj != nil {
		LogrusObj.Out = src
		return
	}
	// 实例化
	logger := logrus.New()
	logger.Out = src                   // 设置输出
	logger.SetLevel(logrus.DebugLevel) // 设置日志规则
	logger.SetFormatter(&logrus.TextFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})
	LogrusObj = logger
}

func setOutPutFile() (*os.File, error) {
	now := time.Now()
	logFilePath := ""
	if dir, err := os.Getwd(); err == nil {
		// os.Getwd()获取当前的工作目录
		logFilePath = dir + "/logs/"
	}
	_, err := os.Stat(logFilePath)
	if os.IsNotExist(err) {
		if err = os.MkdirAll(logFilePath, 0777); err != nil {
			log.Println(err.Error())
			return nil, err
		}
	}
	logFileName := now.Format("2006-01-02") + ".log"
	// 日志文件
	fileName := path.Join(logFilePath, logFileName)
	_, err = os.Stat(fileName)
	if os.IsNotExist(err) {
		if err = os.MkdirAll(fileName, 0777); err != nil {
			// os.MkdirAll是用来创建目录的,而不是文件。应该使用os.Create或os.OpenFile来创建文件
			log.Println(err.Error())
			return nil, err
		}
	}
	// 写入文件
	src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
	if err != nil {
		return nil, err
	}
	return src, nil
}
// util.LogrusObj.Infoln(err)
// controllers/user.go
package controllers

import (
	"fmt"
	pkg "godemo/pkg/logger"

	"github.com/gin-gonic/gin"
)

type UserController struct{}

func (u UserController) GetUserInfo(c *gin.Context) {
	id := c.Param("id")
	name := c.Param("name")
	ReturnSuccess(c, 0, name, id, 1)
}

func (u UserController) GetList(c *gin.Context) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("捕获异常:", err)
			pkg.LogrusObj.Infoln(err)
		}
	}()
	num1 := 1
	num2 := 0
	num3 := num1 / num2
	ReturnErrer(c, 4004, num3)
}

引入Gorm框架

go get -u gorm.io/driver/mysql
go get -u github.com/jinzhu/gorm
net start mysql
mysql -u root -p
net stop mysql

// config/db.go
package config

const (
	Mysqldb = "root:l20030328@tcp(127.0.0.1:3306)/ranking?charset=utf8"
)
// dao/dao.go
package dao

import (
	"godemo/config"
	pkg "godemo/pkg/logger"
	"time"

	"github.com/jinzhu/gorm"
    _ "gorm.io/driver/mysql"
)

var (
	Db  *gorm.DB
	err error
)

func init() {
	Db, err = gorm.Open("mysql", config.Mysqldb)
	if err != nil {
		pkg.LogrusObj.Error(map[string]interface{}{"mysql conent error": err})
	}
	if Db.Error != nil {
		pkg.LogrusObj.Error(map[string]interface{}{"datebase conent error": Db.Error})
	}
	Db.DB().SetMaxIdleConns(10)
	Db.DB().SetMaxOpenConns(100)
	Db.DB().SetConnMaxLifetime(time.Hour)
}

// models/user.go
package models

import "godemo/dao"

type User struct {
	Id   int
	name string
}

func (User) TableName() string {
	return "user"
}

func GetUserTest(id int) (User, error) {
	var user User
	err := dao.Db.Where("id = ?", id).First(&user).Error
	return user, err
}

// controllers/user.go
package controllers

import (
	"fmt"
	"godemo/models"
	"strconv"

	"github.com/gin-gonic/gin"
)

type UserController struct{}

func (u UserController) GetUserInfo(c *gin.Context) {
	idStr := c.Param("id")
	name := c.Param("name")
	id, _ := strconv.Atoi(idStr)

	user, _ := models.GetUserTest(id)

	ReturnSuccess(c, 0, name, user, 1)
}

func (u UserController) GetList(c *gin.Context) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("捕获异常:", err)
		}
	}()
	num1 := 1
	num2 := 0
	num3 := num1 / num2
	ReturnErrer(c, 4004, num3)
}

数据库crud的实现

// models/user.go
package models

import "godemo/dao"

type User struct {
	Id       int
	Username string
}

func (User) TableName() string {
	return "user"
}

func init() {
	dao.Db.AutoMigrate(&User{})
}

func GetUserTest(id int) (User, error) {
	var user User
	err := dao.Db.Where("id = ?", id).First(&user).Error
	return user, err
}

func GetUserListTest() ([]User, error) {
	var users []User
	err := dao.Db.Where("id < ?", 3).Find(&users).Error
	return users, err
}

func AddUser(username string) (int, error) {
	user := User{Username: username}
	err := dao.Db.Create(&user).Error
	return user.Id, err
}

func UpdateUser(id int, username string) {
	dao.Db.Model(&User{}).Where("id = ?", id).Update("username", username)
}

func DeleteUser(id int) error {
	err := dao.Db.Delete(&User{}, id).Error
	return err
}
// controllers/user.go
package controllers

import (
	"fmt"
	"godemo/models"
	"strconv"

	"github.com/gin-gonic/gin"
)

type UserController struct{}

func (u UserController) GetUserInfo(c *gin.Context) {
	idStr := c.Param("id")
	// name := c.Param("name")
	id, _ := strconv.Atoi(idStr)

	user, _ := models.GetUserTest(id)

	ReturnSuccess(c, 0, "name", user, 1)
}

func (u UserController) AddUser(c *gin.Context) {
	username := c.DefaultPostForm("username", "")
	id, err := models.AddUser(username)
	if err != nil {
		ReturnErrer(c, 4002, "保存错误")
		return
	}
	ReturnSuccess(c, 0, "保存成功", id, 1)
}

func (u UserController) UpdateUser(c *gin.Context) {
	username := c.DefaultPostForm("username", "")
	idStr := c.DefaultPostForm("id", "")
	id, _ := strconv.Atoi(idStr)
	models.UpdateUser(id, username)
	ReturnSuccess(c, 0, "更新成功", true, 1)
}

func (u UserController) DeleteUser(c *gin.Context) {
	idStr := c.DefaultPostForm("id", "")
	id, _ := strconv.Atoi(idStr)
	err := models.DeleteUser(id)
	if err != nil {
		ReturnErrer(c, 4003, "删除错误")
		return
	}
	ReturnSuccess(c, 0, "删除成功", true, 1)
}

func (u UserController) GetList(c *gin.Context) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("捕获异常:", err)
		}
	}()
	num1 := 1
	num2 := 0
	num3 := num1 / num2
	ReturnErrer(c, 4004, num3)
}

func (u UserController) GetUserListTest(c *gin.Context) {
	users, err := models.GetUserListTest()
	if err != nil {
		ReturnErrer(c, 4004, "没有相关数据")
		return
	}
	ReturnSuccess(c, 0, "查询成功", users, 1)
}
// router/routers.go
package router

import (
	"godemo/controllers"
	"net/http"

	"github.com/gin-gonic/gin"
)

func Router() *gin.Engine {
	r := gin.Default()

	r.GET("/hello", func(ctx *gin.Context) {
		ctx.String(http.StatusOK, "Hello World")
	})

	user := r.Group("/user")
	{
		user.GET("/info/:id", controllers.UserController{}.GetUserInfo)

		user.POST("/list", controllers.UserController{}.GetList)

		user.POST("/add", controllers.UserController{}.AddUser)
		user.POST("/update", controllers.UserController{}.UpdateUser)

		user.POST("/delete", controllers.UserController{}.DeleteUser)
		user.POST("/list/test", controllers.UserController{}.GetUserListTest)
	}

	order := r.Group("/order")
	{
		order.GET("list", controllers.OrderController{}.GetList)
	}

	return r
}

用户注册登录,以及会话的使用

// controllers/user.go
package controllers

type UserController struct{}
// models/user.go
package models

import "godemo/dao"

type User struct {
	Id       int
	Username string
}

func (User) TableName() string {
	return "user"
}

func init() {
	dao.Db.AutoMigrate(&User{})
}
// router/routers.go
package router

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

func Router() *gin.Engine {
	r := gin.Default()

	user := r.Group("/user")
	{

	}

	return r
}

注册

// contrillers/common.go
package controllers

import (
	"crypto/md5"
	"encoding/hex"

	"github.com/gin-gonic/gin"
)

type JsonStruct struct {
	Code  int         `json:"code"`
	Msg   interface{} `json:"msg"`
	Data  interface{} `json:"data"`
	Count int64       `json:"count"`
}

type JsonErrStruct struct {
	Code int         `json:"code"`
	Msg  interface{} `json:"msg"`
}

func ReturnSuccess(c *gin.Context, code int, msg interface{}, data interface{}, count int64) {
	json := &JsonStruct{
		Code:  code,
		Msg:   msg,
		Data:  data,
		Count: count,
	}
	c.JSON(200, json)
}

func ReturnErrer(c *gin.Context, code int, msg interface{}) {
	json := &JsonErrStruct{
		Code: code,
		Msg:  msg,
	}
	c.JSON(200, json)
}

// md5加密
func EncryMd5(s string) string {
	ctx := md5.New()
	ctx.Write([]byte(s))
	return hex.EncodeToString(ctx.Sum(nil))
}
// controllers/user.go
package controllers

import (
	"godemo/models"

	"github.com/gin-gonic/gin"
)

type UserController struct{}

func (u UserController) Register(c *gin.Context) {
	// 接收用户名,密码,确认密码
	username := c.DefaultPostForm("username", "")
	password := c.DefaultPostForm("password", "")
	confirmPassword := c.DefaultPostForm("confirmPassword", "")
	if username == "" || password == "" || confirmPassword == "" {
		ReturnErrer(c, 4001, "请输入正确信息")
		return
	}
	if password != confirmPassword {
		ReturnErrer(c, 4001, "密码和确认密码不一致")
		return
	}
	user, _ := models.GetUserInfoByUsername(username)
	if user.Id != 0 {
		ReturnErrer(c, 4001, "用户名已存在")
		return
	}
	_, err := models.AddUser(username, EncryMd5(password))
	if err != nil {
		ReturnErrer(c, 4001, "保存失败,请联系管理员")
		return
	}
	ReturnSuccess(c, 1, "注册成功", user.Id, 1)
}
// models/user.go
package models

import (
	"godemo/dao"
	"time"
)

type User struct {
	Id         int    `json:"id"`
	Username   string `json:"username"`
	Password   string `json:"password"`
	AddTime    int64  `json:"addTime"`
	UpdateTime int64  `json:"updateTime"`
}

func (User) TableName() string {
	return "user"
}

func init() {
	dao.Db.AutoMigrate(&User{})
}

// 判断用户名是否存在
func GetUserInfoByUsername(username string) (User, error) {
	var user User
	err := dao.Db.Where("username = ?", username).First(&user).Error
	return user, err
}

// 创建用户
func AddUser(username string, password string) (int, error) {
	user := User{Username: username, Password: password,
		AddTime: time.Now().Unix(), UpdateTime: time.Now().Unix(),
	}
	err := dao.Db.Create(&user).Error
	return user.Id, err
}
// router/routers.go
package router

import (
	"godemo/controllers"

	"github.com/gin-gonic/gin"
)

func Router() *gin.Engine {
	r := gin.Default()

	user := r.Group("/user")
	{
		user.POST("/register", controllers.UserController{}.Register)
	}

	return r
}

登录|会话签发

redis-server.exe  --service-start
net start mysql
net stop mysql
redis-server.exe  --service-stop
go get github.com/gin-contrib/sessions
go get github.com/gin-contrib/sessions/redis
// router/routers.go
package router

import (
	"godemo/config"
	"godemo/controllers"

	"github.com/gin-contrib/sessions"
	sessions_redis "github.com/gin-contrib/sessions/redis"
	"github.com/gin-gonic/gin"
)

func Router() *gin.Engine {
	r := gin.Default()

	store, _ := sessions_redis.NewStore(10, "tcp", config.RedisAddress, "", []byte("secret"))
	// []byte("secret") 是用于加密会话数据的密钥
	r.Use(sessions.Sessions("mysession", store))
	// 将会话中间件添加到路由,检查是否存在名为 "mysession" 的会话,并在不存在时创建一个

	user := r.Group("/user")
	{
		user.POST("/register", controllers.UserController{}.Register)
		user.POST("/login", controllers.UserController{}.Login)
	}

	return r
}
// config/redis.go
package config

const (
	RedisAddress = "localhost:6379"
)
// controllers/user.go
package controllers

import (
	"godemo/models"
	"strconv"

	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
)

type UserController struct{}

// 注册
func (u UserController) Register(c *gin.Context) {
	// 接收用户名,密码,确认密码
	username := c.DefaultPostForm("username", "")
	password := c.DefaultPostForm("password", "")
	confirmPassword := c.DefaultPostForm("confirmPassword", "")
	if username == "" || password == "" || confirmPassword == "" {
		ReturnErrer(c, 4001, "请输入正确信息")
		return
	}
	if password != confirmPassword {
		ReturnErrer(c, 4001, "密码和确认密码不一致")
		return
	}
	user, _ := models.GetUserInfoByUsername(username)
	if user.Id != 0 {
		ReturnErrer(c, 4001, "用户名已存在")
		return
	}
	_, err := models.AddUser(username, EncryMd5(password))
	if err != nil {
		ReturnErrer(c, 4001, "保存失败,请联系管理员")
		return
	}
	ReturnSuccess(c, 1, "注册成功", user.Id, 1)
}

type UserApi struct {
	Id       int    `json:"id"`
	Username string `json:"username"`
}

// 登录
func (u UserController) Login(c *gin.Context) {
	// 接受用户名和密码
	username := c.DefaultPostForm("username", "")
	password := c.DefaultPostForm("password", "")
	if username == "" || password == "" {
		ReturnErrer(c, 4001, "请输入正确的信息")
		return
	}

	user, _ := models.GetUserInfoByUsername(username)
	if user.Id == 0 {
		ReturnErrer(c, 4004, "用户名或密码不正确")
		return
	}
	if user.Password != EncryMd5(password) {
		ReturnErrer(c, 4004, "用户名或密码不正确")
		return
	}
	session := sessions.Default(c)
	// 从请求上下文中获取默认会话
	session.Set("login:"+strconv.Itoa(user.Id), user.Id)
	// 将会话键设置为 "login:" 后跟用户的 ID
	session.Save()
	// 保存会话数据,将数据发送到 Redis 服务器进行存储
	data := UserApi{Id: user.Id, Username: username}
	ReturnSuccess(c, 0, "登录成功", data, 1)
}
// router/routers.go
package router

import (
	"godemo/config"
	"godemo/controllers"

	"github.com/gin-contrib/sessions"
	sessions_redis "github.com/gin-contrib/sessions/redis"
	"github.com/gin-gonic/gin"
)

func Router() *gin.Engine {
	r := gin.Default()

	store, _ := sessions_redis.NewStore(10, "tcp", config.RedisAddress, "", []byte("secret"))
	r.Use(sessions.Sessions("mysession", store))

	user := r.Group("/user")
	{
		user.POST("/register", controllers.UserController{}.Register)
		user.POST("/login", controllers.UserController{}.Login)
	}

	return r
}

另一种的token签发

package routes

import (
	"todo_list/api"
	"todo_list/middieware"

	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
)

func NewRouter() *gin.Engine {
	r := gin.Default() // 创建gin引擎
	store := cookie.NewStore([]byte("something-very-secret"))
	//  初始化cookie会话存储
	r.Use(sessions.Sessions("mysession", store))
	//  设置会话中间件
	v1 := r.Group("api/v1") // 定义一个路由组v1
	{
		// 用户操作,在路由组内定义路由
		v1.POST("user/register", api.UserRegister)
		v1.POST("user/login", api.UserLogin)
		authed := v1.Group("/")
		authed.Use(middieware.JWT())
		// 运行时先验证middieware.JWT()这个中间件看有没有这个权限
		{
			authed.POST("task", api.CreateTask)
		}
	}
	return r
}
// service/user.go
	// 密码验证成功后发一个token,为了其他功能需要身份验证所给前端存储的
	// 创建一个备忘录,这个功能就要token,不然不知道是谁创建的备忘录
	token, err := utils.GenerateToken(user.ID, service.UserName, service.Password)
	if err != nil {
		return serializer.Response{
			Status: 500,
			Msg:    "Token签发错误",
		}
	}
	return serializer.Response{
		Status: 200,
		Data:   serializer.TokenData{User: serializer.BuildUser(user), Token: token},
		Msg:    "登录成功",
	}
}
package middieware

import (
	"time"
	"todo_list/pkg/utils"

	"github.com/gin-gonic/gin"
)

func JWT() gin.HandlerFunc {
	return func(c *gin.Context) {
		code := 200
		token := c.GetHeader("Authorization") // 从HTTP请求的头部获取名为"Authorization"的值,这通常是JWT存放的地方
		if token == "" {
			code = 404
		} else {
			claim, err := utils.ParseToken(token)
			if err != nil {
				code = 403 // 无权限,token是无权的,是假的
			} else if time.Now().Unix() > claim.ExpiresAt {
				code = 401 // Token无效
				// JWT解析成功,但当前时间已经超过了claim.ExpiresAt(即token已过期)
			}
		}
		if code != 200 {
			c.JSON(200, gin.H{
				// map[string]interface{}的缩写
				"status": code,
				"msg":    "Token解析错误",
			})
			c.Abort() // 终止当前的请求处理流程
			return
		}
		c.Next() // 将请求传递给后续的中间件或路由处理函数
	}
}
package utils

import (
	"time"

	"github.com/dgrijalva/jwt-go"
)

var JwtSecret = []byte("ABAB")

type Claims struct {
	Id       uint   `json:"id"`
	UserName string `json:"user_name"`
	Password string `json:"password"`
	jwt.StandardClaims
}

// 签发token
func GenerateToken(id uint, username, password string) (string, error) {
	notTime := time.Now()
	expireTime := notTime.Add(24 * time.Hour)
	Claims := Claims{
		Id:       id,
		UserName: username,
		Password: password,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expireTime.Unix(),
			Issuer:    "todo_list",
		},
	}
	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims)
	token, err := tokenClaims.SignedString(JwtSecret)
	return token, err

}

// 验证token
func ParseToken(token string) (*Claims, error) {
	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(Token *jwt.Token) (interface{}, error) {
		return JwtSecret, nil
	})
	if tokenClaims != nil {
		if Claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
			return Claims, nil
		}
	}
	return nil, err
}

投票功能

redis-server.exe  --service-start
net start mysql
net stop mysql
redis-server.exe  --service-stop

查看player列表

// controllers/player.go
// controllers/player.go
package controllers

import (
	"godemo/models"
	"strconv"

	"github.com/gin-gonic/gin"
)

type PlayerController struct{}

func (p PlayerController) GetPlayers(c *gin.Context) {
	aidStr := c.DefaultPostForm("aid", "0")
	aid, _ := strconv.Atoi(aidStr)

	rs, err := models.GetPlayers(aid)
	if err != nil {
		ReturnErrer(c, 4004, "没有相关信息")
		return
	}
	ReturnSuccess(c, 0, "success", rs, 1)
}
// models/player.go
package models

import "godemo/dao"

type Player struct {
	Id          int    `json:"id"`
	Aid         int    `json:"aid"`
	Ref         string `json:"ref"`
	Nickname    string `json:"nickname"`
	Declaration string `json:"declaration"`
	Avatar      string `json:"avatar"`
	Score       int    `json:"score"`
}

func (Player) TableName() string {
	return "player"
}

func init() {
	dao.Db.AutoMigrate(&Player{})
}

func GetPlayers(aid int) ([]Player, error) {
	var players []Player
	err := dao.Db.Where("aid = ?", aid).Find(&players).Error
	return players, err
}
// router/routers.go
package router

import (
	"godemo/config"
	"godemo/controllers"

	"github.com/gin-contrib/sessions"
	sessions_redis "github.com/gin-contrib/sessions/redis"
	"github.com/gin-gonic/gin"
)

func Router() *gin.Engine {
	r := gin.Default()

	store, _ := sessions_redis.NewStore(10, "tcp", config.RedisAddress, "", []byte("secret"))
	// []byte("secret") 是用于加密会话数据的密钥
	r.Use(sessions.Sessions("mysession", store))
	// 将会话中间件添加到路由,检查是否存在名为 "mysession" 的会话,并在不存在时创建一个

	user := r.Group("/user")
	{
		user.POST("/register", controllers.UserController{}.Register)
		user.POST("/login", controllers.UserController{}.Login)
	}

	player := r.Group("/player")
	{
		player.POST("/list", controllers.PlayerController{}.GetPlayers)
	}

	return r
}

投票实现

// models/user.go
package models

import (
	"godemo/dao"
	"time"
)

type User struct {
	Id         int    `json:"id"`
	Username   string `json:"username"`
	Password   string `json:"password"`
	AddTime    int64  `json:"addTime"`
	UpdateTime int64  `json:"updateTime"`
}

func (User) TableName() string {
	return "user"
}

func init() {
	dao.Db.AutoMigrate(&User{})
}

// 判断用户名是否存在
func GetUserInfoByUsername(username string) (User, error) {
	var user User
	err := dao.Db.Where("username = ?", username).First(&user).Error
	return user, err
}

func GetUserInfo(id int) (User, error) {
	var user User
	err := dao.Db.Where("id = ?", id).First(&user).Error
	return user, err
}

// 创建用户
func AddUser(username string, password string) (int, error) {
	user := User{Username: username, Password: password,
		AddTime: time.Now().Unix(), UpdateTime: time.Now().Unix(),
	}
	err := dao.Db.Create(&user).Error
	return user.Id, err
}
// controllers/vote.go
// controllers/vote.go
package controllers

import (
	"godemo/models"
	"strconv"

	"github.com/gin-gonic/gin"
)

type VoteController struct{}

func (v VoteController) AddVote(c *gin.Context) {
	userIdStr := c.DefaultPostForm("userId", "0")
	playerIdStr := c.DefaultPostForm("playerId", "0")
	userId, _ := strconv.Atoi(userIdStr)
	playerId, _ := strconv.Atoi(playerIdStr)

	if userId == 0 || playerId == 0 {
		ReturnErrer(c, 4001, "请输入正确的信息")
		return
	}
	user, _ := models.GetUserInfo(userId)
	if user.Id == 0 {
		ReturnErrer(c, 4001, "投票用户不存在")
		return
	}
	player, _ := models.GetPlayerInfo(playerId)
	if player.Id == 0 {
		ReturnErrer(c, 4001, "参赛选手不存在")
		return
	}
	vote, _ := models.GetVoteInfo(userId, playerId)
	if vote.Id != 0 {
		ReturnErrer(c, 4001, "已投票")
		return
	}
	rs, err := models.AddVote(userId, playerId)
	if err == nil {
		// 更新参赛选手分数字段,自增1
		models.UpdatePlayerScore(playerId)
		ReturnSuccess(c, 0, "投票成功", rs, 1)
		return
	}
	ReturnErrer(c, 4004, "请联系管理员")
	return
}
// models/player.go
// models/player.go
package models

import (
	"godemo/dao"

	"github.com/jinzhu/gorm"
)

type Player struct {
	Id          int    `json:"id"`
	Aid         int    `json:"aid"`
	Ref         string `json:"ref"`
	Nickname    string `json:"nickname"`
	Declaration string `json:"declaration"`
	Avatar      string `json:"avatar"`
	Score       int    `json:"score"`
}

func (Pla