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

news2024/11/15 18:23:07

gin官方文档

https://gin-gonic.com/docs/quickstart/

1. 安装

go get -u github.com/gin-gonic/gin
https://github.com/gin-gonic/gin

简单入门

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")
}
//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. 路由分组

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

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")
}

案例源码

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. 获取路由分组的参数

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")
}

案例代码

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参数

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参数

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混合

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")
}

在这里插入图片描述
在这里插入图片描述

案例源码

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文件

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

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

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

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")
}

源码案例

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)
}

客户端反解码

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语言实现翻译解释器

"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
}

案例整体源码

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中间件

是一类能够为一种或多种应用程序合作互通、资源共享,同时还能够为该应用程序提供相关的服务的软件。中间件是一类软件统称,而非一种软件;中间件不仅仅实现互连,还要实现应用之间的互操作。
中间件与操作系统和数据库共同构成基础软件三大支柱,是一种应用于分布式系统的基础软件,位于应用与操作系统、数据库之间,为上层应用软件提供开发、运行和集成的平台。中间件解决了异构网络环境下软件互联和互操作等共性问题,并提供标准接口、协议,为应用软件间共享资源提供了可复用的“标准件”。

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) {
//
//}

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

//如果你想不执行后面的逻辑
context.Abort()

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

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

案例源码

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_%E8%AF%AD%E5%8F%A5

1. 设置静态文件路径

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>
 <h1>
 {{ .title }}
 </h1>
</html>

3. templates/posts/index.tmpl

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

4. templates/users/index.tmpl

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

案例源码

{{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/

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("注销服务...")
}

设置静态文件

router.Static("/static", "./static")

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1230665.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“Python+”集成技术高光谱遥感数据处理

高光谱遥感数据处理的基础、python开发基础、机器学习和应用实践。重点解释高光谱数据处理所涉及的基本概念和理论&#xff0c;旨在帮助学员深入理解科学原理。结合Python编程工具&#xff0c;专注于解决高光谱数据读取、数据预处理、高光谱数据机器学习等技术难题&#xff0c;…

PPT基础:编辑顶点

目录 编辑顶点对顶点的编辑对线段的编辑编辑顶点用法 编辑顶点 所在位置&#xff1a; 实质&#xff1a;是一种改变图像性质的操作 如何把一个圆形变成三角形&#xff1a;选中其中一个顶点&#xff0c;右键删除一个顶点&#xff1b;靠近某一条边&#xff0c;右键“拉伸弓形”即…

壹基金为爱同行到余村,以一步步健行换一滴滴净水

为帮助乡村儿童喝上干净的、足量的饮用水,壹基金联合可口可乐中国发起为爱同行2023安吉余村公益健行活动。本次活动得到了湖州市安吉县天荒坪镇人民政府、湖州市安吉县天荒坪镇余村村村民委员会的大力支持,由深圳市登山户外运动协会、文益社、悦跑圈联合主办。参与健行不仅能感…

「Python编程基础」第3章:控制流

文章目录 一、用“炒菜”简单介绍下“控制流”二、布尔值三、比较运算符四、 和 操作符的区别五、布尔操作符六、混合布尔和比较操作符七、代码块是什么&#xff1f;八、控制流语句1. if 语句2. else语句3. elif语句4. 总结 九、while循环语句十、break语句十一、continue语句…

竞赛选题 行人重识别(person reid) - 机器视觉 深度学习 opencv python

文章目录 0 前言1 技术背景2 技术介绍3 重识别技术实现3.1 数据集3.2 Person REID3.2.1 算法原理3.2.2 算法流程图 4 实现效果5 部分代码6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习行人重识别(person reid)系统 该项目…

Windows通过ssh连接远程服务器并进入docker容器

Windows操作系统可以使用vscode通过ssh远程连接ubuntu服务器&#xff1a; 首先下载安装vscode&#xff0c;然后安装插件Remote-SSH&#xff1a; 通过ctlshiftP可以打开Remote-SSH&#xff1a;connect to host&#xff1b; 输入ssh Userhostname -p port host和hostname对应的是…

力扣 2. 两数相加

Problem: 2. 两数相加 思路与算法 Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this…

numpy报错:AttributeError: module ‘numpy‘ has no attribute ‘float‘

报错&#xff1a;AttributeError: module numpy has no attribute float numpy官网&#xff1a;NumPy 报错原因&#xff1a;从numpy1.24起删除了numpy.bool、numpy.int、numpy.float、numpy.complex、numpy.object、numpy.str、numpy.long、numpy.unicode类型的支持。 解决办法…

HarmonyOS开发:ArkTs常见数据类型(一)

前言 无论是Android还是iOS开发&#xff0c;都提供了多种数据类型用于常见的业务开发&#xff0c;但在ArkTs中&#xff0c;数据类型就大有不同&#xff0c;比如int&#xff0c;float&#xff0c;double&#xff0c;long统一就是number类型&#xff0c;当然了也不存在char类型&a…

系列九、JUC强大的辅助类

一、CountDownLatch 1.1、概述 让一些线程阻塞直到另一些线程完成一系列操作后才被唤醒 1.2、功能 CountDownLatch主要有两个方法&#xff0c;当一个或多个线程调用await方法时&#xff0c;这些线程会阻塞&#xff0c;其它线程调用countDown方法会将计数器减1(调用countDown方…

企业实现员工聊天和转账行为的实时监管

如何解决企业营销团队的管理问题&#xff1f; 在当今竞争激烈的市场环境中&#xff0c;企业营销团队的管理显得尤为重要。营销团队是企业发展的重要支柱&#xff0c;然而&#xff0c;一些常见的问题如员工飞单、私单、辱骂删除客户、离职带走公司客户以及工作不认真、工作量无…

Java笔记-PrintWriter无文件或文件内容为空

代码是这样的&#xff1a; PrintWriter out new PrintWriter(fileName); out.println(object.getValue()); out.close(); 查了下这个类&#xff0c;出现问题不会报异常。 但可以用checkError()函数返回值&#xff0c;判断是否有错误。 如果发现无文件或者文件内容为空&#…

C# Onnx DIS高精度图像二类分割

目录 介绍 效果 模型信息 项目 代码 下载 介绍 github地址&#xff1a;https://github.com/xuebinqin/DIS This is the repo for our new project Highly Accurate Dichotomous Image Segmentation 对应的paper是ECCV2022的一篇文章《Highly Accurate Dichotomous Imag…

猫罐头品牌排行榜盘点!猫罐头哪个牌子好?

很多猫主人会发现他们家的猫咪可能对猫粮感到腻了&#xff0c;或者猫咪平时不爱喝水&#xff0c;还有一些主人可能会注意到猫咪太瘦了&#xff0c;想尝试给它们添加一些猫罐头&#xff0c;但又不确定如何选择。目前市场上的猫罐头品牌众多&#xff0c;确实让人有些困惑。那么&a…

Cesium 问题:输出的 纬度 latitude 是 0

文章目录 问题分析问题 在坐标转换的时候,出现如下问题 分析 在检查代码后,发现我将转换之前的高度默认设置为了 0 ,因此没能正确转换 let positionsOnCircle = [];// 圆面边缘的点 let numPoints = 360; for (let i

OpenAI | 解雇奥特曼后 ChatGPT将何去何从

大家好,我是极智视界,欢迎关注我的公众号,获取我的更多前沿科技分享 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码和资源下载,链接:https://t.zsxq.com/0aiNxERDq 11 月 17 日,OpenAI 董事会突然宣布解雇联合创始人兼首席执行官 Sam Altman,原因是…

【计算机组成体系结构】存储系统基本概念

一、存储器的层次化结构 CPU无法直接与辅存传输数据&#xff0c;因为CPU的处理速度大于辅存的读写速度&#xff0c;如果直接交互会拖累CPU的速度。所以需要先把辅存中的数据写入主存再与CPU进行数据传输。 Cache&#xff0c;高速缓存在主存和CPU之间。存在Cache的原因是主存的…

静态文件鉴权

​ 静态文件鉴权的解决方案 背景介绍 XX业务系统作为BXX业务系统的孪生姐妹系统&#xff0c;是对BXX受理业务的强力补充系统&#xff0c;他允许操作员拿着IPAD&#xff0c;和客户约定地点上门受理业务。 因一些业务的受理&#xff0c;按照最新的业务规章制度&#xff0c;需…

React中封装echarts图表组件以及自适应窗口变化

文章目录 前言环境代码接口使用效果后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;react.js &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现错误&#xff0c;…

二手车选购参考:Honda CB650R/CBR650R 2019~2023 国产价格享进口本田

本田好&#xff0c;本田妙&#xff0c;本田大法呱呱叫。本田的650系列在国内可以说是伤透了一批老车主的心&#xff0c;上路12万&#xff0c;3年现在还值个4万&#xff0c;搁谁也受不了&#xff0c;正好没什么太好的新闻写&#xff0c;今天就带大家来看看二手车选购参考&#x…