gin相关操作--一起学习921190764

gin官方文档

go 复制代码
https://gin-gonic.com/docs/quickstart/

1. 安装

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

简单入门

go 复制代码
package main

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

func pong(c *gin.Context) {
	//c.JSON(http.StatusOK, gin.H{
	//	"message": "pong",
	//})
	//第二种
	c.JSON(http.StatusOK, map[string]string{
		"message": "pong",
	})
}
func main() {
	//实例化一个gin的server对象
	r := gin.Default()

	r.GET("/ping", pong)
	r.Run(":8084") // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
go 复制代码
//restful 的开发中
	router.GET("/someGet", getting)
	router.POST("/somePost", posting)
	router.PUT("/somePut", putting)
	router.DELETE("/someDelete", deleting)
	router.PATCH("/somePatch", patching)
	router.HEAD("/someHead", head)
	router.OPTIONS("/someOptions", options)

1. 路由分组

go 复制代码
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1")
{
	v1.POST("/login", loginEndpoint)
	v1.POST("/submit", submitEndpoint)
	v1.POST("/read", readEndpoint)
}
// Simple group: v2
v2 := router.Group("/v2")
{
	v2.POST("/login", loginEndpoint)
	v2.POST("/submit", submitEndpoint)
	v2.POST("/read", readEndpoint)
}
router.Run(":8082")
}

2. 带参数的url

go 复制代码
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
		"message": "pong",
		})
	})
	r.GET("/user/:name/:action/", func(c *gin.Context) {
	name := c.Param("name")
	action := c.Param("action")
	c.String(http.StatusOK, "%s is %s", name, action)
	})
	r.GET("/user/:name/*action", func(c *gin.Context) {
	name := c.Param("name")
	action := c.Param("action")
	c.String(http.StatusOK, "%s is %s", name, action)
	})

r.Run(":8082")
}

案例源码

go 复制代码
package main

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

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

	goodsGroup := router.Group("/goods")
	//不需要依附于任何函数体
	{
		goodsGroup.GET("/list", goodsList)

		//goodsGroup.GET("/1", goodsDetail) //获取商品id为1的详细信息
		//带参的url
		//goodsGroup.GET("/:id/:action", goodsDetail) //获取商品id为1的详细信息
		goodsGroup.GET("/:id/*action", goodsDetail) //获取商品id为1的详细信息 带*就会把id后面全部的路径全部取出来
		goodsGroup.POST("/add", createGoods)
	}
	router.Run(":8082")
}

func createGoods(context *gin.Context) {

}

func goodsDetail(context *gin.Context) {
	id := context.Param("id")
	action := context.Param("action")
	context.JSON(http.StatusOK, gin.H{
		"id":     id,
		"action": action,
	})
}

func goodsList(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"name": "goodlist",
	})
}

3. 获取路由分组的参数

go 复制代码
package main
import "github.com/gin-gonic/gin"
type Person struct {
	ID string `uri:"id" binding:"required,uuid"`
	Name string `uri:"name" binding:"required"`
}
func main() {
	route := gin.Default()
	route.GET("/:name/:id", func(c *gin.Context) {
		var person Person
		if err := c.ShouldBindUri(&person); err != nil {
		c.JSON(400, gin.H{"msg": err})
		return
	}
	c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
	})
	route.Run(":8088")
}

案例代码

go 复制代码
package main

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

// Person 这是来约束参数是什么类型
type Person struct {
	//Id   string `uri:"id" binding:"required,uuid"` //这里必须是uuid http://127.0.0.1:8083/bobby/6e4e2015-a5c2-9279-42a6-6b70478276bc
	Id   int    `uri:"id" binding:"required"`
	Name string `uri:"name" binding:"required"`
}

func main() {

	router := gin.Default()
	router.GET("/:name/:id", func(context *gin.Context) {
		var person Person
		if err := context.ShouldBindUri(&person); err != nil {
			context.Status(404)
		}
		context.JSON(http.StatusOK, gin.H{
			"name": person.Name,
			"id":   person.Id,
		})
	})

	router.Run(":8083")
}

1. 获取get参数

go 复制代码
func main() {
router := gin.Default()
// 匹配的url格式: /welcome?firstname=Jane&lastname=Doe
router.GET("/welcome", func(c *gin.Context) {
	firstname := c.DefaultQuery("firstname", "Guest")
	lastname := c.Query("lastname") // 是 c.Request.URL.Query().Get("lastname
	c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
	})
	router.Run(":8080")
}

2. 获取post参数

go 复制代码
func main() {
	router := gin.Default()
	router.POST("/form_post", func(c *gin.Context) {
	message := c.PostForm("message")
	nick := c.DefaultPostForm("nick", "anonymous") // 此⽅法可以设置默认值
		c.JSON(200, gin.H{
		"status": "posted",
		"message": message,
		"nick": nick,
		})
	})
	router.Run(":8080")
}

3. get、post混合

go 复制代码
POST /post?id=1234&page=1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=manu&message=this_is_great
func main() {
	router := gin.Default()
	router.POST("/post", func(c *gin.Context) {
		id := c.Query("id")
		page := c.DefaultQuery("page", "0")
		name := c.PostForm("name")
		message := c.PostForm("message")
		fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, mes
		})
	router.Run(":8080")
}


案例源码

go 复制代码
package main

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

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

	//GET请求获取参数
	router.GET("/welcome", welcome)

	//POST获取参数
	router.POST("/form_post", formPost)

	//get和post请求混合使用
	router.POST("/post", getPost)

	router.Run(":8083")
}

func getPost(context *gin.Context) {
	id := context.Query("id")
	page := context.DefaultQuery("page", "0")
	name := context.PostForm("name")
	message := context.DefaultPostForm("message", "信息")
	context.JSON(http.StatusOK, gin.H{
		"id":      id,
		"page":    page,
		"name":    name,
		"message": message,
	})
}

// http://127.0.0.1:8083/form_post 然后在body写入参数
func formPost(context *gin.Context) {
	message := context.PostForm("message")
	nick := context.DefaultPostForm("nick", "anonymous")
	context.JSON(http.StatusOK, gin.H{
		"message": message,
		"nick":    nick,
	})
}

// http://127.0.0.1:8083/welcome
// 如果什么都不写取默认值 为bobby 和chengpeng
// http://127.0.0.1:8083/welcome?firstname=chengpeng2&lastname=llAS
// 如果这种写法 得到的就是chengpeng2 和llAS
func welcome(context *gin.Context) {
	firstName := context.DefaultQuery("firstname", "bobby")
	lastName := context.DefaultQuery("lastname", "chengpeng")
	context.JSON(http.StatusOK, gin.H{
		"first_name": firstName,
		"last_name":  lastName,
	})
}

1. 输出json和protobuf

新建user.proto文件

proto 复制代码
syntax = "proto3";
option go_package = ".;proto";
message Teacher {
 string name = 1;
 repeated string course = 2;
}

protoc --go_out=. --go-grpc_out=. .\user.proto

go 复制代码
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"start/gin_t/proto"
)
func main() {
	r := gin.Default()
	// gin.H is a shortcut for map[string]interface{}
	r.GET("/someJSON", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
	})
	r.GET("/moreJSON", func(c *gin.Context) {
		// You also can use a struct
		var msg struct {
		Name string `json:"user"` //转义
		Message string
		Number int
		}
		msg.Name = "Lena"
		msg.Message = "hey"
		msg.Number = 123
		// Note that msg.Name becomes "user" in the JSON
		// Will output : {"user": "Lena", "Message": "hey", "Number": 123}
		c.JSON(http.StatusOK, msg)
	})
	r.GET("/someProtoBuf", func(c *gin.Context) {
		courses := []string{"python", "django", "go"}
		data:&proto.Teacher{
		Name: "bobby",
		Course: courses,
	}
	// Note that data becomes binary data in the response
	// Will output protoexample.Test protobuf serialized data
	c.ProtoBuf(http.StatusOK, data)
	})
	// Listen and serve on 0.0.0.0:8080
	r.Run(":8083")
}

2. PureJSON

通常情况下,JSON会将特殊的HTML字符替换为对应的unicode字符,比如 < 替换为 \u003c ,如果想原样输出html,则使用PureJSON

go 复制代码
func main() {
	r := gin.Default()
	// Serves unicode entities
	r.GET("/json", func(c *gin.Context) {
	c.JSON(200, gin.H{
	"html": "<b>Hello, world!</b>",
	})
	})
	// Serves literal characters
	r.GET("/purejson", func(c *gin.Context) {
	c.PureJSON(200, gin.H{
	"html": "<b>Hello, world!</b>",
	})
	})
	// listen and serve on 0.0.0.0:8080
	r.Run(":8080")
}

源码案例

go 复制代码
package main

import (
	proto1 "GormStart/gin_start/ch05/proto"
	"github.com/gin-gonic/gin"
	"net/http"
)

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

	//JSON
	router.GET("/moreJSON", moreJSON)
	//proto
	router.GET("/someProtoBuf", returnProto)

	router.Run(":8083")
}

func returnProto(context *gin.Context) {
	course := []string{"python", "go", "微服务"}
	user := &proto1.Teacher{
		Name:   "bobby",
		Course: course,
	}

	context.ProtoBuf(http.StatusOK, user)

}

//	{
//	   "user": "bobby",
//	   "Message": "这是测试一个json",
//	   "Number": 20
//	}
//
// http://127.0.0.1:8083/moreJSON
func moreJSON(context *gin.Context) {
	var msg struct {
		Name    string `json:"user"`
		Message string
		Number  int
	}

	msg.Name = "bobby"
	msg.Message = "这是测试一个json"
	msg.Number = 20

	context.JSON(http.StatusOK, msg)
}

客户端反解码

go 复制代码
package main

import (
	proto1 "GormStart/gin_start/ch05/proto"
	"fmt"
	"google.golang.org/protobuf/proto"
	"io/ioutil"
	"net/http"
)

func main() {
	resp, _ := http.Get("http://127.0.0.1:8083/someProtoBuf")
	bytes, _ := ioutil.ReadAll(resp.Body)
	var res proto1.Teacher
	_ = proto.Unmarshal(bytes, &res)
	fmt.Println(res.Name, res.Course)
}

1. 表单的基本验证

若要将请求主体绑定到结构体中,请使用模型绑定,目前支持JSON、XML、YAML和标准表单值(foo=bar&boo=baz)的绑定。
Gin使用 go-playground/validator和https://github.com/go-playground/validator 验证参数,查看完整文档(https://pkg.go.dev/github.com/go-playground/validator/v10)。

需要在绑定的字段上设置tag,比如,绑定格式为json,需要这样设置 json:"fieldname" 。此外,Gin还提供了两套绑定方法:
Must bind

  • Methods - Bind , BindJSON , BindXML , BindQuery , BindYAML
    Behavior - 这些方法底层使用 MustBindWith ,如果存在绑定错误,请求将被以下指令中c.AbortWithError(400,err).SetType(ErrorTypeBind) ,响应状态代码会被设置为400,请求头 Content-Type 被设置为 text/plain;charset=utf-8 。注意,如果你试图在此之后设置响应代码,将会发出一个警告 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422 ,如果你希望更好地控制行为,请使用 ShouldBind 相关的方法

Should bind

  • Methods - ShouldBind(动态决定JSON,XML等等) , ShouldBindJSON , ShouldBindXML ,ShouldBindQuery , ShouldBi ndYAML
  • Behavior - 这些方法底层使用 ShouldBindWith ,如果存在绑定错误,则返回错误,开发人员 可以正确处理请求和错误。
    当我们使用绑定方法时,Gin会根据Content-Type推断出使用哪种绑定器,如果你确定你绑定的是什么,你可以使用 MustBindWith 或者 BindingWith 。
    你还可以给字段指定特定规则的修饰符,如果一个字段用 binding:"required" 修饰,并且在绑定时该字段的值为空,那么将返回一个错误。

validator支持中文==>国际化

go语言实现翻译解释器

go 复制代码
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
	
var trans ut.Translator

// InitTrans 翻译
func InitTrans(locale string) (err error) {
	//修改gin框架中的validator引擎属性,实现定制
	//Engine返回为StructValidator实现提供动力的底层验证器引擎。
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		zhT := zh.New() //中文翻译器
		enT := en.New() //英文翻译器
		// 第一个参数是备用(fallback)的语言环境  // 后面的参数是应该支持的语言环境(支持多个)
		uni := ut.New(enT, zhT, enT) //后面可以重复放
		// locale 通常取决于 http 请求头的 'Accept-Language'
		//根据参数取翻译器实例
		// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找
		trans, ok = uni.GetTranslator(locale) //拿到Translator
		if !ok {
			return fmt.Errorf("uni.GetTranslator(%s)", locale)
		}
		// 注册翻译器
		switch locale {
		case "en":
			en_translations.RegisterDefaultTranslations(v, trans) //使用英文的注册器
		case "zh":
			zh_translations.RegisterDefaultTranslations(v, trans) //使用中文注册器
		default:
			en_translations.RegisterDefaultTranslations(v, trans)
		}

		return
	}
	return
}

案例整体源码

go 复制代码
package main

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

// LoginForm 绑定为json
type LoginForm struct {
	//form json xml
	User     string `form:"user" json:"user" xml:"user" binding:"required,min=3,max=10"` //required必填最短长度
	Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

// SignUpForm 注册
type SignUpForm struct {
	Age        uint8  `json:"age" binding:"gte=1,lte=130"`
	Name       string `json:"name" binding:"required,min=3"`
	Email      string `json:"email" binding:"required,email"` //email是否是合法的格式
	Password   string `json:"password" binding:"required"`
	RePassword string `json:"re_password" binding:"required,eqfield=Password"` //跨字段验证 eqfield指定上面的字段和它相等
}

var trans ut.Translator

// InitTrans 翻译
func InitTrans(locale string) (err error) {
	//修改gin框架中的validator引擎属性,实现定制
	//Engine返回为StructValidator实现提供动力的底层验证器引擎。
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		//注册一个获取json的tag的自定义方法
		v.RegisterTagNameFunc(func(field reflect.StructField) string {
			//name1 := strings.SplitN(field.Tag.Get("json"), ",", 2)
			//fmt.Println("chengpeng", name1)
			//a := field.Tag.Get("form")
			//fmt.Println("chengpeng", a)

			name := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]
			if name == "_" {
				return ""
			}
			return name
		})
		zhT := zh.New() //中文翻译器
		enT := en.New() //英文翻译器
		// 第一个参数是备用(fallback)的语言环境  // 后面的参数是应该支持的语言环境(支持多个)
		uni := ut.New(enT, zhT, enT) //后面可以重复放
		// locale 通常取决于 http 请求头的 'Accept-Language'
		//根据参数取翻译器实例
		// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找
		trans, ok = uni.GetTranslator(locale) //拿到Translator
		if !ok {
			return fmt.Errorf("uni.GetTranslator(%s)", locale)
		}
		// 注册翻译器
		switch locale {
		case "en":
			en_translations.RegisterDefaultTranslations(v, trans) //使用英文的注册器
		case "zh":
			zh_translations.RegisterDefaultTranslations(v, trans) //使用中文注册器
		default:
			en_translations.RegisterDefaultTranslations(v, trans)
		}

		return
	}
	return
}

//	"msg": {	"LoginForm.user": "user长度必须至少为3个字符" }
//
// 去掉LoginForm
func removeTopStruct(fileds map[string]string) map[string]string {
	rsp := map[string]string{}

	for filed, err := range fileds {
		//要查找的字符串.的位置strings.Index(filed, ".")
		rsp[filed[strings.Index(filed, ".")+1:]] = err
	}
	return rsp
}

func main() {

	err := InitTrans("zh")
	if err != nil {
		fmt.Println("获取翻译器错误")
		return
	}

	router := gin.Default()
	router.POST("/loginJSON", func(context *gin.Context) {
		var loginForm LoginForm
		//你应该这样 获取参数
		err := context.ShouldBind(&loginForm)
		if err != nil {
			errs, ok := err.(validator.ValidationErrors) //转换为FieldError
			if !ok {
				context.JSON(http.StatusOK, gin.H{
					"msg": err.Error(),
				})
			}
			//fmt.Println(err.Error())
			context.JSON(http.StatusBadRequest, gin.H{
				//"msg": err.Error(),
				//"msg": errs.Translate(trans),
				"msg": removeTopStruct(errs.Translate(trans)),
			})
			return
		}
		context.JSON(http.StatusOK, gin.H{
			"msg": "登录成功",
		})
	})

	router.POST("/signup", func(context *gin.Context) {
		var signUpForm SignUpForm
		//你应该这样 获取参数
		err := context.ShouldBind(&signUpForm)

		if err != nil {
			errs, ok := err.(validator.ValidationErrors) //转换为FieldError
			//不能转换成功
			if !ok {
				context.JSON(http.StatusOK, gin.H{
					"msg": err.Error(),
				})
			}
			context.JSON(http.StatusBadRequest, gin.H{
				//"msg": err.Error(),
				//"msg": errs.Translate(trans),
				"msg": removeTopStruct(errs.Translate(trans)),
			})
			return
		}
		context.JSON(http.StatusOK, gin.H{
			"msg": "注册成功",
		})
	})
	_ = router.Run(":8083")

}

中间件==>自定义gin中间件

是一类能够为一种或多种应用程序合作互通、资源共享,同时还能够为该应用程序提供相关的服务的软件。中间件是一类软件统称,而非一种软件;中间件不仅仅实现互连,还要实现应用之间的互操作。

中间件与操作系统和数据库共同构成基础软件三大支柱,是一种应用于分布式系统的基础软件,位于应用与操作系统、数据库之间,为上层应用软件提供开发、运行和集成的平台。中间件解决了异构网络环境下软件互联和互操作等共性问题,并提供标准接口、协议,为应用软件间共享资源提供了可复用的"标准件"。

go 复制代码
package main

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

// MyLogger 自定义中间件
func MyLogger() gin.HandlerFunc {

	return func(context *gin.Context) {
		now := time.Now()
		// 设置变量到Context的key中,可以通过Get()取
		context.Set("example", "123456")
		//让原本该执行的逻辑继续执行
		context.Next()

		//把开始的时间给我去计算时长
		end := time.Since(now)
		//拿到状态信息   // 中间件执行完后续的一些事情
		status := context.Writer.Status()
		//[GIN-debug] Listening and serving HTTP on :8083
		//耗时:%!V(time.Duration=610500)
		//状态 200
		fmt.Printf("耗时:%V\n", end)
		fmt.Println("状态", status)
	}
}

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

	router.Use(MyLogger())

	router.GET("/ping", func(context *gin.Context) {
		context.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})

	router.Run(":8083")
}

//func main() {
//	//engine.Use(Logger(), Recovery()) 默认使用这两个中间件
//	//router := gin.Default()
//	router := gin.New()
//	//使用logger中间件和recovery(恢复)中间件 全局使用
//	router.Use(gin.Logger(), gin.Recovery())
//
//	//某一组url 这样配置这个中间件只有这样开始的时候,这个url才会影响
//	authrized := router.Group("/goods")
//	authrized.Use(AuthRequired)
//
//}
//
 AuthRequired 中间件
//func AuthRequired(context *gin.Context) {
//
//}

终止中间件后续的逻辑的执行

go 复制代码
//如果你想不执行后面的逻辑
context.Abort()

为什么连return都阻止不了后续逻辑的执行?

那是因为Use或者GET等等里面有一个HandlersChain的切片(type HandlersChain []HandlerFunc)添加到切片中去,如果使用return只是返回这个函数,并不会结束全部的接口。使用Next函数,index只是跳转到下个函数里面,如果使用Abort他会把index放到切片最后,那么全部都会结束。

案例源码

go 复制代码
package main

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

// MyLogger 自定义中间件
func MyLogger() gin.HandlerFunc {

	return func(context *gin.Context) {
		now := time.Now()
		// 设置变量到Context的key中,可以通过Get()取
		context.Set("example", "123456")

		//让原本该执行的逻辑继续执行
		context.Next()

		//把开始的时间给我去计算时长
		end := time.Since(now)
		//拿到状态信息   // 中间件执行完后续的一些事情
		status := context.Writer.Status()

		fmt.Printf("耗时:%V\n", end)
		fmt.Println("状态", status)
	}
}

func TokenRequired() gin.HandlerFunc {
	return func(context *gin.Context) {
		var token string

		//token放到了Header里面
		for k, v := range context.Request.Header {
			if k == "X-Token" {
				token = v[0]
				fmt.Println("chengpeng", token)
			} else {
				fmt.Println(k, v)
			}
			//fmt.Println(k, v, token)
		}

		if token != "bobby" {
			context.JSON(http.StatusUnauthorized, gin.H{
				"msg": "未登录",
			})
			//return结束不了
			//return
			//如果你想不执行后面的逻辑
			context.Abort()
		}
		context.Next()
	}
}

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

	//router.Use(MyLogger())

	router.Use(TokenRequired())
	router.GET("/ping", func(context *gin.Context) {
		context.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})

	router.Run(":8083")
}

//func main() {
//	//engine.Use(Logger(), Recovery()) 默认使用这两个中间件
//	//router := gin.Default()
//	router := gin.New()
//	//使用logger中间件和recovery(恢复)中间件 全局使用
//	router.Use(gin.Logger(), gin.Recovery())
//
//	//某一组url 这样配置这个中间件只有这样开始的时候,这个url才会影响
//	authrized := router.Group("/goods")
//	authrized.Use(AuthRequired)
//
//}
//
 AuthRequired 中间件
//func AuthRequired(context *gin.Context) {
//
//}

gin返回html

官方地址:https://golang.org/pkg/html/template/

翻 译 : https://colobu.com/2019/11/05/Golang-Templates-Cheatsheet/#if/else_语句

1. 设置静态文件路径

go 复制代码
package main
import (
 "net/http"
 "github.com/gin-gonic/gin"
)
func main() {
	 // 创建⼀个默认的路由引擎
	 r := gin.Default()
	 // 配置模板
	 r.LoadHTMLGlob("templates/**/*")
	 //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html
	 // 配置静态⽂件夹路径 第⼀个参数是api,第⼆个是⽂件夹路径
	 r.StaticFS("/static", http.Dir("./static"))
	 // GET:请求⽅式;/hello:请求的路径
	 // 当客户端以GET⽅法请求/hello路径时,会执⾏后⾯的匿名函数
	 r.GET("/posts/index", func(c *gin.Context) {
		 // c.JSON:返回JSON格式的数据
		 c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
		 "title": "posts/index",
		 })
	 })
	 r.GET("gets/login", func(c *gin.Context) {
		 c.HTML(http.StatusOK, "posts/login.tmpl", gin.H{
		 "title": "gets/login",
		 })
	 })
	 // 启动HTTP服务,默认在0.0.0.0:8080启动服务
	 r.Run()
}

2. index.html内容

html 复制代码
<html>
 <h1>
 {{ .title }}
 </h1>
</html>

3. templates/posts/index.tmpl

html 复制代码
{{ define "posts/index.tmpl" }}
<html><h1>
 {{ .title }}
</h1>
<p>Using posts/index.tmpl</p>
</html>
{{ end }}

4. templates/users/index.tmpl

html 复制代码
{{ define "users/index.tmpl" }}
<html><h1>
 {{ .title }}
</h1>
<p>Using users/index.tmpl</p>
</html>
{{ end }}

案例源码

html 复制代码
{{define "goods/list.html"}}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>商品名称</title>
    </head>
    <body>
        <h1>商品列表页</h1>
    </body>
    </html>
{{end}}
{{define "users/list.html"}}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>用户列表页</title>
    </head>
    <body>
        <h1>用户列表页</h1>
    </body>
    </html>
{{end}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{.name}}
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>
        {{.title}}
    </h1>
</body>
</html>


优雅退出: https://gin-gonic.com/zh-cn/docs/examples/graceful-restart-or-stop/

go 复制代码
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"os"
	"os/signal"
	"syscall"
)

func main() {
	//优雅退出,当我们关闭程序的时候,应该做的后续处理
	//微服务 启动之前或者启动之后会做一件事,将当前的服务的ip地址和端口号注册到注册中心
	//我们当前的服务停止了以后并没有告知注册中心
	router := gin.Default()

	router.GET("/", func(context *gin.Context) {
		context.JSON(http.StatusOK, gin.H{
			"msg": "pong",
		})
	})

	go func() {
		router.Run(":8083") //启动以后会一直停在这里
	}()

	//如果想要接收到信号 kill -9 强杀命令
	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	//处理后续的逻辑
	fmt.Println("关闭server中...")
	fmt.Println("注销服务...")
}

设置静态文件

go 复制代码
router.Static("/static", "./static")
相关推荐
刀法如飞2 天前
一款Go语言Gin框架MVC脚手架,满足大部分场景
go·mvc·gin
花酒锄作田3 天前
Gin 框架中的规范响应格式设计与实现
golang·gin
西岸行者3 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意3 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码3 天前
嵌入式学习路线
学习
毛小茛3 天前
计算机系统概论——校验码
学习
babe小鑫3 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms3 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下3 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。3 天前
2026.2.25监控学习
学习