【Go · Gin】基础知识

Gin

1. Gin 介绍

Gin框架是一个基于Go语言的轻量级Web框架,对Go语言的HTTP包进一步封装,使得Web开发更加便捷高效。

2. 原生http库

通过以下代码可创建一个Go语言内置HTTP请求。

go 复制代码
package main

import (
	"fmt"
	"io"
	"net/http"
)

// 处理方法
func Index(writer http.ResponseWriter, request *http.Request) {
	fmt.Println(request.Method, request.URL.String())
	if request.Method != "GET" {
		byteData, _ := io.ReadAll(request.Body)
		fmt.Println(string(byteData))
	}
	fmt.Println("REQUEST HEADER: ", request.Header)
	fmt.Println("REQUEST METHOD: ", request.Method)
	fmt.Println("REQUEST URL: ", request.URL)
	fmt.Println("REQUEST MULTIPART_FORM: ", request.MultipartForm)
	fmt.Println("REQUEST REQUEST_URI: ", request.RequestURI)

	writer.Write([]byte("Hello HTTP!"))
}

func main() {
	http.HandleFunc("/index", Index)
	fmt.Println("HTTP Server Start Running 127.0.0.1:8080")
	http.ListenAndServe(":8080", nil)
}

Index函数中参数所属方法:

启动编写的单请求Web服务。

使用CURL命令发送GET请求。

使用Apifox发送GET请求。

3. Gin框架

为解决原生Go HTTP库繁琐问题(请求方式/JSON单独判断、错误返回),在此基础上编写了Gin框架。

3.1 导入Gin

安装三方库

go 复制代码
go get github.com/gin-gonic/gin

访问失败

更换三方源

配置国内源命令 & 最好记忆的三方源(七牛云)

go 复制代码
go env -w GOPROXY=https://goproxy.cn,direct

三方源

go 复制代码
https://goproxy.cn,direct
https://mirrors.aliyun.com/goproxy/
https://goproxy.tuna.tsinghua.edu.cn

访问成功

3.2 了解Gin

Gin程序固定步骤:初始化 → 路由 → 监听运行

Context结构体

编写简单服务

go 复制代码
package main

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

type Response struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data any    `json:"data"`
}

// 处理方法
func Index(c *gin.Context) {
	c.JSON(200, Response{
		Code: 0,
		Msg:  "成功",
		Data: map[string]any{},
	})

}

func main() {
	// 1. 初始化
	r := gin.Default()
	// 2. 挂载路由
	r.GET("/index", Index)
	// 3. 绑定端口 运行
	r.Run(":8080")
}
go 复制代码
package main

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

type Response struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data any    `json:"data"`
}

// 处理方法
func Index(c *gin.Context) {
	c.JSON(200, Response{
		Code: 0,
		Msg:  "成功",
		Data: map[string]any{},
	})

}

func main() {
	// 1. 初始化
    gin.setMode("release") 		// 关闭DEBUG日志
	r := gin.Default()
	// 2. 挂载路由
	r.GET("/index", Index)
	// 3. 绑定端口 运行
    r.Run(":8080")				// 内网运行 --> 等价于r.Run("0.0.0.0:8080")
    // r.Run("127.0.0.1:8080")	// 本机运行 --> 等价于r.Run("0.0.0.0:8080")
}

4. 响应:文件 - 封装

4.1 响应JSON

目前大部分前后端交互都是以JSON为主。

但是一般不会直接返回JSON,会对其进行封装,定义一种标准的返回格式:code / data /msg

前端可以根据code判断操作是否成功,不同公司不同定义。

go 复制代码
package main

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

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

	r.GET("/index", func(c *gin.Context) {
		c.JSON(200, gin.H{"code": 0, "msg": "成功", "data": gin.H{}})
	})
	r.Run()
}

一般除服务器软硬件错误,状态码默认为200,业务操作逻辑利用Response中的业务码code进行表示。

在res包内定义enter.go表示响应体及其附属方法。

go 复制代码
package res

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

type Response struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data any    `json:"data"`
}

var codeMap = map[int]string{
	1001: "权限错误",
	1002: "角色错误",
}

func response(c *gin.Context, code int, data any, msg string) {
	c.JSON(200, Response{
		Code: 0,
		Msg:  msg,
		Data: data,
	})
}

func Ok(c *gin.Context, data any, msg string) {
	response(c, 2001, data, msg)
}

func OkWithData(c *gin.Context, data any) {
	response(c, 2001, data, "成功")
}

func OkWithMsg(c *gin.Context, msg string) {
	response(c, 2001, gin.H{}, msg)
}

func Fail(c *gin.Context, code int, data any, msg string) {
	response(c, code, data, msg)
}

func FailWithMsg(c *gin.Context, msg string) {
	response(c, 1001, gin.H{}, msg)
}

func FailWithCode(c *gin.Context, code int) {
	msg, ok := codeMap[code]
	if !ok {
		msg = "服务错误"
	}
	response(c, code, gin.H{}, msg)
}

Gin代码

go 复制代码
package main

import (
	"gin_study/response/res"
	"github.com/gin-gonic/gin"
)

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

	r.GET("/index", func(c *gin.Context) {
		res.OkWithMsg(c, "成功进入首页!")
	})

	r.GET("/user", func(c *gin.Context) {
		res.OkWithData(c, map[string]any{
			"UserName": "Mike",
			"Gender":   0,
		})
	})
	r.Run()
}

利用Apifox发起GET请求

服务器响应GET请求

4.2 响应html

go 复制代码
package main

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

func main() {
	r := gin.Default()
	r.LoadHTMLGlob("response/templates/*")
	// r.LoadHTMLFiles("response/templates/index.html")
	r.GET("", func(c *gin.Context) {
		c.HTML(200, "index.html", nil)
	})
	r.Run(":8080")
}
go 复制代码
package main

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

func main() {
	r := gin.Default()
	r.LoadHTMLGlob("response/templates/*")
	// r.LoadHTMLFiles("response/templates/index.html")
	r.GET("", func(c *gin.Context) {
		c.HTML(200, "index.html", map[string]any{
			"title":   "新的首页",
			"context": "内容",
		})
	})
	r.Run(":8080")
}

关于部署:

  • 前端单独部署,后端单独部署
  • 前端打包完成,后端统一部署

4.3 响应文件

用于浏览器直接发起下载请求
go 复制代码
c.Header("Content-Type", "application/octet-stream")
c.Header("Content-Disposition", "attachment; filename=index.html")
c.File("response/templates/index.html")

完整代码

go 复制代码
package main

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

func main() {
	r := gin.Default()
	// r.LoadHTMLFiles("response/templates/index.html")
	r.GET("", func(c *gin.Context) {
		c.Header("Content-Type", "application/octet-stream")
		c.Header("Content-Disposition", "attachment; filename=index.html")
		c.File("response/templates/index.html")
	})
	r.Run(":8080")
}

当指定文件不存在时则会出现404

前端请求后端接口,唤起浏览器下载
go 复制代码
c.Header("filename", "xxx.png")
c.Header("msg", "文件下载成功")
c.File("response/templates/xxx.png")

唤起本质

go 复制代码
<a href="文件地址" download="文件名">文件下载</a>

前端调用下载接口请求,后端返回文件下载地址而不直接返回文件,前端构造需点击元素唤起下载。

4.4 静态文件

静态文件路径不能再被路由使用

go 复制代码
package main

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

func main() {
	r := gin.Default()
	r.Static("st", "response/static")
    // r.StaticFile("abx", "response/static/abc.txt")
	r.Run(":8080")
}

5. 请求:参数/文件 - 接口

5.1 查询参数

查询参数

  • 示例:?key1=value1&key1=value2&key3=value3(同一key可重复多个value)
  • 并非GET请求专属
go 复制代码
package main

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

func main() {
	r := gin.Default()
	r.GET("", func(c *gin.Context) {
		name := c.Query("name")
		gender := c.DefaultQuery("gender", "22")
		books := c.QueryArray("books")
		fmt.Println(name, gender, books)
	})
	r.Run(":8080")
}

5.2 动态参数

用户个人信息

go 复制代码
/users?id=123   // 查询参数模式
/users/123      // 动态参数模式
go 复制代码
package main

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

func main() {
	r := gin.Default()
	r.GET("users/:id/:name", func(c *gin.Context) {
		id := c.Param("id")
		name := c.Param("name")

		fmt.Println(id, name)
	})
	r.Run(":8080")
}

5.3 表单参数

POST请求Form表单

go 复制代码
package main

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

func main() {
	r := gin.Default()
	r.POST("users", func(c *gin.Context) {
		id := c.PostForm("id")
		name, nameOk := c.GetPostForm("name")

		fmt.Println(id)
		fmt.Println(name, nameOk)
	})
	r.Run(":8080")
}

5.5 文件上传

单文件上传

go 复制代码
package main

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

func main() {
	r := gin.Default()
	r.POST("users", func(c *gin.Context) {
		fileHeader, err := c.FormFile("file")
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println("File Header : ", fileHeader.Filename) // 文件名
		fmt.Println("File Size   : ", fileHeader.Size)     // 文件大小 单位字节

		file, _ := fileHeader.Open()    // 第二个参数是error类型
		byteData, _ := io.ReadAll(file) // 第二个参数是error类型
		err = os.WriteFile("xxx.png", byteData, 0777)
		fmt.Println(err)
	})
	r.Run(":8080")
}

对上传的文件的封装方法如下。

go 复制代码
//file, _ := fileHeader.Open()    // 第二个参数是error类型
//byteData, _ := io.ReadAll(file) // 第二个参数是error类型
//err = os.WriteFile("xxx.png", byteData, 0777)
//fmt.Println(err)

// 等效代码
err = c.SaveUploadedFile(fileHeader, "uploads/yyy/"+fileHeader.Filename)
fmt.Println(err)

多文件上传

go 复制代码
package main

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

func main() {
	r := gin.Default()
	r.POST("users", func(c *gin.Context) {
		form, err := c.MultipartForm()
		if err != nil {
			fmt.Println(err)
			return
		}
		for _, headers := range form.File {
			for _, header := range headers {
				c.SaveUploadedFile(header, "uploads/zzz/"+header.Filename)
			}
		}
	})
	r.Run(":8080")
}

5.6 原始内容

不同请求体对应的原始内容

body阅后即焚

go 复制代码
package main

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

func main() {
	r := gin.Default()
	r.POST("", func(c *gin.Context) {
		byteData, _ := io.ReadAll(c.Request.Body)
		fmt.Println(string(byteData))
		// Body阅后即焚,使用bytes.NewReader可解决此问题
		c.Request.Body = io.NopCloser(bytes.NewReader(byteData))
		name := c.PostForm("name")
		fmt.Println("name", name)
	})
	r.Run(":8080")
}

form-data

go 复制代码
----------------------------001246823629557183212734
Content-Disposition: form-data; name="name"

你好
----------------------------001246823629557183212734
Content-Disposition: form-data; name="age"

32
----------------------------001246823629557183212734--

x-www-form-urlencoded

url编码

go 复制代码
name=abc&age=35

json

go 复制代码
{
    "name": "xyz",
    "age": 12
}

※ 接口测试工具

常用工具:Postman / Apifox

  • get加请求体
  • ws加请求头

6. bind绑定器

6.1 绑定参数

使用binding可以完成参数绑定,不能对x-www-form-urlencoded进行解析。

6.1.1 查询参数
go 复制代码
package bind

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

func main() {
   r := gin.Default()
   r.GET("", func(c *gin.Context) {
      type User struct {
         Name string `form:"name"`
         Age  int    `form:"age"`
      }
      var user User
      err := c.ShouldBindQuery(&user)
      fmt.Println(user, err)
   })
   r.Run(":8080")
}
6.1.2 路径参数
go 复制代码
package bind

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

func main() {
   r := gin.Default()
    r.GET("users/:id/:name", func(c *gin.Context) {
      type User struct {
         Name string `uri:"name"`
         ID  int     `uri:"id"`
      }
      var user User
      err := c.ShouldBindQuery(&user)
      fmt.Println(user, err)
   })
   r.Run(":8080")
}
6.1.3 表单参数
go 复制代码
package bind

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

func main() {
   r := gin.Default()
    r.POST("form", func(c *gin.Context) {
      type User struct {
         Name string `form:"name"`
         ID  int     `form:"id"`
      }
      var user User
      err := c.ShouldBind(&user)
      fmt.Println(user, err)
   })
   r.Run(":8080")
}
6.1.4 json参数
go 复制代码
package main

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

func main() {
	r := gin.Default()
	r.GET("json", func(c *gin.Context) {
		type User struct {
			Name string `json:"name"`
			Age  int    `json:"age"`
		}
		var user User
		err := c.ShouldBindJSON(&user)
		fmt.Println(user, err)
	})
	r.Run(":8080")
}
6.1.5 header参数
go 复制代码
package main

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

func main() {
	r := gin.Default()
	r.GET("header", func(c *gin.Context) {
		type User struct {
			Name      string `header:"Name"`
			Age       int    `header:"Age"`
			UserAgent string `header:"UserAgent"`
		}
		var user User
		err := c.ShouldBindHeader(&user)
		fmt.Println(user, err)
	})
	r.Run(":8080")
}

6.2 binding内置规则

多条不同规则可以以逗号间隔

go 复制代码
// 全局
require	必填字段 不为空+不消失
- 		忽略字段 binding:"-" 或 不写

// 字符串
min		最小长度 binding:"min=5"
max		最大长度 binding:"max=5"
len		长度 binding:"len=5"
contains  包含
excludes  不包含
startwith 字符串前缀
endswith  字符串后缀

// 数字
eq		等于 binding:"eq=3"
ne		等于
gt		大于		
ge		大于等于
lt		小于
lte		小于等于

// 同级字段
eqfield	等于其他字段的值   Password string `binding:"eqfield=Password"`
nefield	不等于其他字段的值

// 网络验证
ip
ipv4
ipv6
uri Identifier 	统一资源标识符 标识唯一资源
url Locator    	统一资源定位符 提供资源路径
email			邮箱校验

// 日期验证
datetime=2026-01-26

// 枚举(仅限设定的选项)
oneof=item1 item2

// 校验数组
dive

6.3 binding错误信息显示中文

go 复制代码
package main

import (
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/zh"
	"github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	zh_translations "github.com/go-playground/validator/v10/translations/zh"
	"strings"
)

var trans ut.Translator

func init() {
	// 创建翻译器
	uni := ut.New(zh.New())
	trans, _ = uni.GetTranslator("zh")

	// 注册翻译器
	v, ok := binding.Validator.Engine().(*validator.Validate)
	if ok {
		_ = zh_translations.RegisterDefaultTranslations(v, trans)
	}
}

func ValidateErr(err error) string {
	errs, ok := err.(validator.ValidationErrors)
	if !ok {
		return err.Error()
	}

	var list []string
	for _, e := range errs {
		list = append(list, e.Translate(trans))
	}
	return strings.Join(list, ";")
}

type User struct {
	Name  string `json:"name" binding:"required"`
	Email string `json:"email" binding:"required,email"`
}

func main() {
	r := gin.Default()
	// 注册路由
	r.POST("/user", func(c *gin.Context) {
		var user User
		if err := c.ShouldBindJSON(&user); err != nil {
			// 参数验证失败
			c.String(200, ValidateErr(err))
		}
	})

	// 启动HTTP服务器
	r.Run()
}

6.4 binding显示错误字段及信息

返回字符串

go 复制代码
package main

import (
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/zh"
	"github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	zh_translations "github.com/go-playground/validator/v10/translations/zh"
	"reflect"
	"strings"
)

var trans ut.Translator

func init() {
	// 创建翻译器
	uni := ut.New(zh.New())
	trans, _ = uni.GetTranslator("zh")

	// 注册翻译器
	v, ok := binding.Validator.Engine().(*validator.Validate)
	if ok {
		_ = zh_translations.RegisterDefaultTranslations(v, trans)
	}

	v.RegisterTagNameFunc(func(field reflect.StructField) string {
		label := field.Tag.Get("label")
		if label == "" {
			return field.Name
		}
		return label
	})
}

func ValidateErr(err error) string {
	errs, ok := err.(validator.ValidationErrors)
	if !ok {
		return err.Error()
	}

	var list []string
	for _, e := range errs {
		list = append(list, e.Translate(trans))
	}
	return strings.Join(list, ";")
}

type User struct {
	Name  string `json:"name" binding:"required" label:"用户名"`
	Email string `json:"email" binding:"required,email" label:"邮箱"`
}

func main() {
	r := gin.Default()
	// 注册路由
	r.POST("/user", func(c *gin.Context) {
		var user User
		if err := c.ShouldBindJSON(&user); err != nil {
			// 参数验证失败
			c.String(200, ValidateErr(err))
		}
	})

	// 启动HTTP服务器
	r.Run(":8080")
}

JSON化

go 复制代码
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/zh"
	"github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	zh_translations "github.com/go-playground/validator/v10/translations/zh"
	"reflect"
	"strings"
)

var trans ut.Translator

func init() {
	// 创建翻译器
	uni := ut.New(zh.New())
	trans, _ = uni.GetTranslator("zh")

	// 注册翻译器
	v, ok := binding.Validator.Engine().(*validator.Validate)
	if ok {
		_ = zh_translations.RegisterDefaultTranslations(v, trans)
	}

	v.RegisterTagNameFunc(func(field reflect.StructField) string {
		label := field.Tag.Get("label")
		if label == "" {
			return field.Name
		}
		attr := field.Tag.Get("json")
		label = fmt.Sprintf("%s--%s", attr, label)
		return label
	})
}

func ValidateErr(err error) any {
	errs, ok := err.(validator.ValidationErrors)
	if !ok {
		return err.Error()
	}

	var _list []string
	var m = map[string]any{}
	for _, e := range errs {
		msg := e.Translate(trans)
		_list = strings.Split(msg, "--")
		m[_list[0]] = _list[1]
	}
	return m
}

type User struct {
	Name  string `json:"name" binding:"required" label:"用户名"`
	Email string `json:"email" binding:"required,email" label:"邮箱"`
}

func main() {
	r := gin.Default()
	// 注册路由
	r.POST("/user", func(c *gin.Context) {
		var user User
		if err := c.ShouldBindJSON(&user); err != nil {
			// 参数验证失败
			c.JSON(200, map[string]any{
				"code": 7,
				"msg":  "验证错误",
				"data": ValidateErr(err),
			})
		}
	})

	// 启动HTTP服务器
	r.Run(":8080")
}

7.自定义验证

go 复制代码
v.RegisterValidation("fip", func(fl validator.FieldLevel) bool {

这种验证方式参数fl的方法如下

go 复制代码
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/zh"
	"github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	zh_translations "github.com/go-playground/validator/v10/translations/zh"
	"net"
	"net/http"
	"reflect"
	"strings"
)

var trans ut.Translator

func init() {
	// 创建翻译器
	uni := ut.New(zh.New())
	trans, _ = uni.GetTranslator("zh")

	// 注册翻译器
	v, ok := binding.Validator.Engine().(*validator.Validate)
	if ok {
		_ = zh_translations.RegisterDefaultTranslations(v, trans)
	}

	v.RegisterTagNameFunc(func(field reflect.StructField) string {
		label := field.Tag.Get("label")
		if label == "" {
			return field.Name
		}
		attr := field.Tag.Get("json")
		label = fmt.Sprintf("%s--%s", attr, label)
		return label
	})

	v.RegisterValidation("fip", func(fl validator.FieldLevel) bool {
		fmt.Println("fl.Field() : ", fl.Field())
		fmt.Println("fl.FieldName() : ", fl.FieldName())
		fmt.Println("fl.StructFieldName() : ", fl.StructFieldName())
		fmt.Println("fl.Parent() : ", fl.Parent())
		fmt.Println("fl.Top() : ", fl.Top()) // 顶层 JSON内部可以嵌套很多层
		fmt.Println("fl.Param() : ", fl.Param())

		ip, ok := fl.Field().Interface().(string)
		if ok && ip != "" {
			ipObj := net.ParseIP(ip)
			return ipObj != nil
		}
		return false
	})
}

func ValidateErr(err error) any {
	errs, ok := err.(validator.ValidationErrors)
	if !ok {
		return err.Error()
	}

	var _list []string
	var m = map[string]any{}
	for _, e := range errs {
		msg := e.Translate(trans)
		_list = strings.Split(msg, "--")
		m[_list[0]] = _list[1]
	}
	return m
}

type User struct {
	Ip string `json:"ip" binding:"fip" label:"ip地址"`
}

func main() {
	r := gin.Default()
	// 注册路由
	r.POST("/user", func(c *gin.Context) {
		var user User
		if err := c.ShouldBindJSON(&user); err != nil {
			// 参数验证失败
			c.JSON(200, map[string]any{
				"code": 7,
				"msg":  "验证错误",
				"data": ValidateErr(err),
			})
		}
		c.JSON(http.StatusOK, user)
	})

	// 启动HTTP服务器
    r.Run(":8080")
}


8. 路由

路由规范

go 复制代码
r := gin.Default()

r.POST()		// 提交数据
r.GET()			// 获取/下载数据
r.PUT()			// 全量更新数据
r.PATCH()		// 增量更新数据
r.DELETE()		// 删除数据
r.any()			//【支持所有】

r.Group("api")	// 分组

路由组可以重名以区分是否添加中间件

go 复制代码
apiGroup := r.Group("api")
apiGroup.Use()
UserGroup(apiGroup)

noMiddleWareGroup := r.Group("api")
LoginGroup(noMiddleWareGroup)

9. 中间件

可以近似理解为 "过滤器"、"拦截器",可以有多个中间件,作用:拦截 / 处理 / 放行。

9.1 局部中间件【PPT示意图】

go 复制代码
package main

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

func Home(c *gin.Context) {
	fmt.Println("Home")
	c.String(200, "Home")
}

// 局部中间件1
func MiddleWare1(c *gin.Context) {
	fmt.Println("MiddleWare 1 请求部分")
	c.Next()
	fmt.Println("MiddleWare 1 响应部分")
}
// 局部中间件2
func MiddleWare2(c *gin.Context) {
	fmt.Println("MiddleWare 2 请求部分")
	c.Next()
	fmt.Println("MiddleWare 2 响应部分")
}

func main() {
	r := gin.Default()
	r.GET("", MiddleWare1, MiddleWare2, Home)
	r.Run(":8080")
}

9.2 全局中间件

全局即路由组,这使得中间件设置更为便捷。

go 复制代码
package main

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

func Home(c *gin.Context) {
	fmt.Println("Home")
	c.String(200, "Home")
}
// 全局中间件1
func GroupMiddleWare1(c *gin.Context) {
	fmt.Println("GroupMiddleWare 1 请求")
	c.Next()
	c.String(200, "MiddleWare 1 响应")
	return
}
// 全局中间件2
func GroupMiddleWare2(c *gin.Context) {
	fmt.Println("GroupMiddleWare 2 请求")
	c.Next()
	c.String(200, "MiddleWare 2 响应")
	return
}
func main() {
	r := gin.Default()
	g := r.Group("api")
    g.Use(GroupMiddleWare1, GroupMiddleWare2)
	// 等同于 g.Use(GroupMiddleWare1) + g.Use(GroupMiddleWare2)
	g.GET("users", Home)
	r.Run(":8080")
}

9.3 中间件操作

9.3.1 中间件放行
go 复制代码
func MiddleWare1(c *gin.Context) {
	fmt.Println("MiddleWare 1 请求部分")
	c.Next()
	fmt.Println("MiddleWare 1 响应部分")
}

func MiddleWare2(c *gin.Context) {
	fmt.Println("MiddleWare 2 请求部分")
	c.Next()
	fmt.Println("MiddleWare 2 响应部分")
}
9.3.2 中间件中断
go 复制代码
func MiddleWare1(c *gin.Context) {
	fmt.Println("MiddleWare 1 请求部分")
	c.Abort()
	fmt.Println("MiddleWare 1 响应部分")
}

func MiddleWare2(c *gin.Context) {
	fmt.Println("MiddleWare 2 请求部分")
	c.Next()
	fmt.Println("MiddleWare 2 响应部分")
}
9.3.3 中间件响应
go 复制代码
func MiddleWare1(c *gin.Context) {
	fmt.Println("MiddleWare 1 请求部分")
	c.Abort()
	c.String(200, "MiddleWare 1 响应")
	return
}

func MiddleWare2(c *gin.Context) {
	fmt.Println("MiddleWare 2 请求部分")
	c.Next()
	fmt.Println("MiddleWare 2 响应部分")
}

9.3 中间件传参

go 复制代码
package main

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

func Home(c *gin.Context) {
	fmt.Println("Home")
	c.Get("name")
	c.Get("classmate")
	c.String(200, "Home")
}

func GroupMiddleWare1(c *gin.Context) {
	fmt.Println("GroupMiddleWare 1 请求")
	// 可以传递所有Go支持类型
	c.Set("name", "Your Dream Name")
	c.Next()
	c.String(200, "MiddleWare 1 响应")
	return
}
func GroupMiddleWare2(c *gin.Context) {
	fmt.Println("GroupMiddleWare 2 请求")
	c.Set("classmate", "Your Dream Classmate")
	c.Next()
	c.String(200, "MiddleWare 2 响应")
	return
}

func main() {
	r := gin.Default()
	g := r.Group("api")
	g.Use(GroupMiddleWare1, GroupMiddleWare2)
	g.GET("users", Home)
	r.Run(":8080")
}

10. Gin框架项目

简单模板

go 复制代码
├── cmd/                   # 项目入口目录
│   └── main.go           # 主程序入口
├── internal/             # 私有代码目录
│   ├── handler/         # HTTP处理器(Controller)
│   │   └── user.go
│   ├── service/         # 业务逻辑层
│   │   └── user.go
│   ├── repository/      # 数据访问层(DAO)
│   │   └── user.go
│   └── model/          # 数据模型
│       └── user.go
├── pkg/                 # 可被外部引用的包
│   ├── middleware/     # 中间件
│   ├── config/        # 配置
│   └── utils/         # 工具函数
├── api/                # API文档(Swagger/OpenAPI)
│   └── swagger.json
├── configs/            # 配置文件目录
│   └── config.yaml
├── scripts/           # 构建、部署脚本
├── test/             # 测试文件目录
└── go.mod            # Go模块文件

复杂模板

go 复制代码
project-root-directory/
├── cmd/
│   └── main.go
├── config/
│   ├── config.yaml
│   └── ...
├── handlers/
│   └── hello.go
├── internal/
│   ├── pkg/
│   │   ├── mypackage/
│   │   │   ├── mypackage.go
│   │   │   └── mypackage_test.go
│   │   └── ...
│   └── ...
├── models/
│   └── user.go
├── routes/
│   └── routes.go
├── repository/
│   ├── rediskeys/
│   │   └── ...
│   ├── couchbaseQuery/
│   │   └── ...
│   └── ...
├── services/
│   └── ...
├── utils/
│   └── ...
├── static/
├── templates/
├── tests/
│   ├── unit/
│   │   └── hello_test.go
│   └── ...
├── go.mod
├── go.sum
├── README.md
└── ...
  • cmd :存放程序的入口文件,通常是main.go
  • config :存放配置文件,如config.yaml
  • handlers:处理请求和响应的处理器,类似于Spring Boot中的controller层。
  • internal:存放项目特定的包,这些包不打算被外部应用程序或库使用。
  • models:定义数据库模型和数据结构,类似于DAO层。
  • routes:定义路由和中间件,组织项目的路由逻辑。
  • repository:包含数据库访问和数据检索逻辑,封装与数据库的交互。
  • services:业务逻辑层,处理具体的业务逻辑。
  • utils:存放工具函数和帮助函数,用于代码复用。
  • static:存放静态资源,如CSS、JavaScript文件和图片。
  • templates:存放HTML模板文件。
  • tests:存放测试代码,如单元测试。

两个经典模板

  • Go-Clean-Template:遵循清晰架构的Gin项目模板 Star数7k+

  • Gin-Vue-Admin:完整的前后端分离权限管理系统 由Gin+Vue实现 Star数24k+

相关推荐
掘根3 小时前
【消息队列项目】客户端四大模块实现
开发语言·后端·ruby
疯狂的挖掘机9 小时前
记一次基于QT的图片操作处理优化思路(包括在图上放大缩小,截图,画线,取值等)
开发语言·数据库·qt
cnxy1889 小时前
围棋对弈Python程序开发完整指南:步骤4 - 提子逻辑和劫争规则实现
开发语言·python·机器学习
意趣新10 小时前
C 语言源文件从编写完成到最终生成可执行文件的完整、详细过程
c语言·开发语言
李艺为11 小时前
根据apk包名动态修改Android品牌与型号
android·开发语言
黄河滴滴11 小时前
java系统变卡变慢的原因是什么?从oom的角度分析
java·开发语言
老华带你飞11 小时前
农产品销售管理|基于java + vue农产品销售管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
superman超哥11 小时前
Rust Workspace 多项目管理:单体仓库的优雅组织
开发语言·rust·多项目管理·rust workspace·单体仓库
kylezhao201912 小时前
C#通过HSLCommunication库操作PLC用法
开发语言·c#