Gin 初步使用

快速入门

官方文档

引入

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

func main() {
	//实例化一个gin的对象
	r := gin.Default()
	r.GET("/ping", pong)
	r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}

访问

http://127.0.0.1:8080/ping

请求处理

//实例化一个gin的对象
//r := gin.Default() //默认会开启两个中间件,logger和recovery错误恢复
func main() {
	// 禁用控制台颜色
	// gin.DisableConsoleColor()

	// 使用默认中间件(logger 和 recovery 中间件)创建 gin 路由
	router := gin.Default()

	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)

	// 默认在 8080 端口启动服务,除非定义了一个 PORT 的环境变量。
	router.Run()
	// router.Run(":3000") hardcode 端口号
}

RESTFUL

路由分组


package main

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

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

	// 简单的路由组: v1
	v1 := router.Group("/v1")
	{
		v1.POST("/login", loginEndpoint)
		v1.POST("/submit", submitEndpoint)
		v1.POST("/read", readEndpoint)
	}

	// 简单的路由组: v2
	v2 := router.Group("/v2")
	{
		v2.POST("/login", loginEndpoint)
		v2.POST("/submit", submitEndpoint)
		v2.POST("/read", readEndpoint)
	}

	router.Run(":8080")
}

例如商品组

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)
		goodsGroup.POST("/add", goodsAdd)
	} //方便查看,逻辑组织在一起

	router.Run(":8080")
}

func goodsAdd(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"message": "add successful",
	})
}

func goodsDetail(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"1": "details",
	})
}

func goodsList(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"Goods": []int{1, 2, 3},
	})
}

url参数解析

关键

router.GET("/user/:name", func(c *gin.Context) {}
package main

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

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

	goodsGroup := router.Group("/goods")
	{
		goodsGroup.GET("", goodsList)
		goodsGroup.GET("/:id", goodsDetail)
		goodsGroup.POST("", goodsAdd)
	} //方便查看,逻辑组织在一起

	router.Run(":8080")
}

func goodsAdd(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"message": "add successful",
	})
}

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

func goodsList(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"Goods": []int{1, 2, 3},
	})
}

约束参数类型

package main

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

type Person struct {
	ID   string    `uri:"id" binding:"required,uuid"`
	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)
			return
		}
		context.JSON(http.StatusOK, gin.H{
			"name": person.Name,
			"id":   person.ID,
		})
	})

	router.Run(":8080")
}

如果使用

http://127.0.0.1:8080/tom/1

则报错404

如果使用

http://127.0.0.1:8080/tom/d4054e14-7479-a070-bf9f-04cfa7bf75c9

则正常

如果整数id,约束

type Person struct {
	ID   int    `uri:"id" binding:"required`
	Name string `uri:"name" binding:"required"`
}

Get参数

package main

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

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

	router.GET("/welcome", welcome)

	router.Run(":8080")
}

func welcome(context *gin.Context) {
	firstName := context.DefaultQuery("firstname", "Tom")
	lastName := context.DefaultQuery("lastname", "Steve")
	context.JSON(http.StatusOK, gin.H{
		"firstName": firstName,
		"lastName":  lastName,
	})
}

访问

http://127.0.0.1:8080/welcome?firstname=Jack&lastname=H

有默认值

POST参数

package main

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

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

	router.GET("/welcome", welcome)
	router.POST("/form_post", formPost)
	router.Run(":8080")
}

func formPost(context *gin.Context) {
	message := context.PostForm("message")
	nick := context.DefaultPostForm("nick", "anonymous")
	context.JSON(http.StatusOK, gin.H{
		"message": message,
		"nick":    nick,
	})
}

func welcome(context *gin.Context) {
	firstName := context.DefaultQuery("firstname", "Tom")
	lastName := context.DefaultQuery("lastname", "Steve")
	context.JSON(http.StatusOK, gin.H{
		"firstName": firstName,
		"lastName":  lastName,
	})
}

混合,即url带参数

func getPost(context *gin.Context) {
	id := context.Query("id")
	name := context.PostForm("name")
	context.JSON(http.StatusOK, gin.H{
		"id":   id,
		"name": name,
	})
}

返回数据格式

package main

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

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

	// gin.H 是 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) {
		// 你也可以使用一个结构体
		var msg struct {
			Name    string `json:"user"`
			Message string
			Number  int
		}
		msg.Name = "Lena"
		msg.Message = "hey"
		msg.Number = 123
		// 注意 msg.Name 在 JSON 中变成了 "user"
		// 将输出:{"user": "Lena", "Message": "hey", "Number": 123}
		c.JSON(http.StatusOK, msg)
	})

	r.GET("/someXML", func(c *gin.Context) {
		c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
	})

	r.GET("/someYAML", func(c *gin.Context) {
		c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
	})

	r.GET("/someProtoBuf", func(c *gin.Context) {
		reps := []int64{int64(1), int64(2)}
		label := "test"
		// protobuf 的具体定义写在 testdata/protoexample 文件中。
		data := &protoexample.Test{
			Label: &label,
			Reps:  reps,
		}
		// 请注意,数据在响应中变为二进制数据
		// 将输出被 protoexample.Test protobuf 序列化了的数据
		c.ProtoBuf(http.StatusOK, data)
	})

	// 监听并在 0.0.0.0:8080 上启动服务
	r.Run(":8080")
}

反解Protobuf使用,proto.Unmarshal

表单验证

gin使用了validator库,https://github.com/go-playground/validator

使用时,需要在要绑定的所有字段上,设置相应的tag。

Gin提供了两类绑定方法:

  • Must bind
    • Methods - Bind, BindJSON, BindXML, BindQuery, BindYAML
    • Behavior - 这些方法属于 MustBindWith 的具体调用。 响应状态码被设置为 400 。
  • Should bind
    • Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML
    • Behavior - 这些方法属于 ShouldBindWith 的具体调用。 如果发生绑定错误,Gin 会返回错误并由开发者处理错误和请求。

使用 Bind 方法时,Gin 会尝试根据 Content-Type 推断如何绑定。 如果你明确知道要绑定什么,可以使用 MustBindWithShouldBindWith

你也可以指定必须绑定的字段。 如果一个字段的 tag 加上了 binding:"required",但绑定时是空值, Gin 会报错。

需要支持什么格式,则指定什么约束

package main

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

// 绑定 JSON
type Login struct {
	User     string `json:"user" binding:"required"`
	Password string `json:"password" binding:"required"`
}

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"`
	Password   string `json:"password" binding:"required"`
	RePassword string `json:"rePassword" binding:"required,eqfield=Password"`
}

func main() {
	router := gin.Default()
	router.POST("/loginJSON", func(context *gin.Context) {
		var loginForm Login
		if err := context.ShouldBind(&loginForm); err != nil {
			fmt.Println(err.Error())
			context.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error(),
			})
			return
		}
		context.JSON(http.StatusOK, gin.H{
			"user":     loginForm.User,
			"password": loginForm.Password,
		})
	})

	router.POST("/signup", func(context *gin.Context) {
		var signForm SignUpForm
		if err := context.ShouldBind(&signForm); err != nil {
			fmt.Println(err.Error())
			context.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error(),
			})
			return
		}
		context.JSON(http.StatusOK, gin.H{
			"message": "注册成功",
		})

	})
	router.Run(":8080")
}

POST http://127.0.0.1:8080/signup
{
    "age":130,
    "name":"Tom",
    "email":"123456789@163.com",
    "password":"1234",
    "rePassword":"1234"
}

中文提示信息;字段问题

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

// 绑定 JSON
type Login struct {
	User     string `json:"user" binding:"required,max=10"`
	Password string `json:"password" binding:"required"`
}

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"`
	Password   string `json:"password" binding:"required"`
	RePassword string `json:"rePassword" binding:"required,eqfield=Password"`
}

var trans ut.Translator

func removeTopStruct(fileds map[string]string) map[string]string {
	res := map[string]string{}
	for filed, err := range fileds {
		res[filed[strings.IndexAny(filed, ".")+1:]] = err
	}
	return res
}

func InitTrans(locale string) (err error) {
	//修改gin中的validator
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {

		//注册一个获取json等待tag的自定义方法;用tag标签的名称
		v.RegisterTagNameFunc(func(field reflect.StructField) string {
			name := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]
			if name == "-" {
				return ""
			}
			return name
		})

		zhT := zh.New()
		enT := en.New()
		uni := ut.New(enT, zhT, enT) //第一个是首选,第二个是备用
		trans, ok = uni.GetTranslator(locale)
		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
}

func main() {

	if err := InitTrans("zh"); err != nil {
		fmt.Println("初始化翻译器错误")
		return
	}

	router := gin.Default()
	router.POST("/loginJSON", func(context *gin.Context) {
		var loginForm Login
		if err := context.ShouldBind(&loginForm); err != nil {
			errs, ok := err.(validator.ValidationErrors)
			if !ok {
				fmt.Println("not ok")
				context.JSON(http.StatusOK, gin.H{
					"error": err.Error(),
				})
				return
			}
			context.JSON(http.StatusOK, gin.H{
				"error": removeTopStruct(errs.Translate(trans)),
			})
			return
		}
		context.JSON(http.StatusOK, gin.H{
			"user":     loginForm.User,
			"password": loginForm.Password,
		})
	})

	router.POST("/signup", func(context *gin.Context) {
		var signForm SignUpForm
		if err := context.ShouldBind(&signForm); err != nil {
			fmt.Println(err.Error())
			context.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error(),
			})
			return
		}
		context.JSON(http.StatusOK, gin.H{
			"message": "注册成功",
		})

	})
	router.Run(":8080")
}

自定义中间件

中间件使用

router := gin.New()

//全局
//使用logger中间件
router.Use(gin.Logger())
//使用recovery
router.Use(gin.Recovery())

//局部使用
authorized := router.Group("/goods")
authorized.Use(AuthRequred)

有一个执行队列,return只是当前中间件结束,

所有要使用context.Abort()

package main

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

func MyLogger() gin.HandlerFunc {
	return func(context *gin.Context) {
		t := time.Now()

		context.Set("example", "123456")
		context.Next()

		end := time.Since(t)

		fmt.Println("时间消耗: ", end)

		status := context.Writer.Status()
		fmt.Println("状态 ", status)
	}
}
func TokenRequired() gin.HandlerFunc {
	return func(context *gin.Context) {
		var token string
		for k, v := range context.Request.Header {
			if k == "X-Token" {
				token = v[0]

				if token != "Tom" {
					context.JSON(http.StatusUnauthorized, gin.H{
						"msg": "未登录",
					})
					//return不能阻止
					context.Abort()
				}
			}
		}
		context.Next()

	}

}
func main() {

	router := gin.Default()

	router.Use(TokenRequired())

	router.GET("/ping", func(context *gin.Context) {

		context.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})

	router.Run(":8080")
}

设置静态文件和HTML文件

package main

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

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

	//相对目录
	router.LoadHTMLFiles("templates/index.tmpl")

	router.GET("/", func(context *gin.Context) {
		context.HTML(http.StatusOK, "index.tmpl", gin.H{
			"Title": "Hello",
		})
	})
	router.Run(":8080")
}

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

在命令行访问。

二级目录,与同名模板

package main

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

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

	//相对目录
	//router.LoadHTMLFiles("templates/index.tmpl", "templates/goods.html")
	router.LoadHTMLGlob("templates/**/*")

	router.GET("/", func(context *gin.Context) {
		context.HTML(http.StatusOK, "index.tmpl", gin.H{
			"Title": "Hello",
		})
	})
	router.GET("/goods", func(context *gin.Context) {
		context.HTML(http.StatusOK, "goods.html", gin.H{
			"name": "一些商品",
		})
	})
	router.GET("/goods/list", func(context *gin.Context) {
		context.HTML(http.StatusOK, "goods/list.html", gin.H{})
	})
	router.GET("/user/list", func(context *gin.Context) {
		context.HTML(http.StatusOK, "user/list.html", gin.H{})
	})
	router.Run(":8080")
}

{{define "goods/list.html"}}

{{end}}

静态文件

对应

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

<link rel="stylesheet" href="/static/style.css">

退出程序

用协程,管道

package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"

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

func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		time.Sleep(5 * time.Second)
		c.String(http.StatusOK, "Welcome Gin Server")
	})

	srv := &http.Server{
		Addr:    ":8080",
		Handler: router,
	}

	go func() {
		// 服务连接
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\n", err)
		}
	}()

	// 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)
	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt)
	<-quit
	log.Println("Shutdown Server ...")

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatal("Server Shutdown:", err)
	}
	log.Println("Server exiting")
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Gin框架使用JWT(JSON Web Token)可以实现份验证和授权功能。JWT是一种用于在网络应用间传递信息的安全方法,它由三部分组成:header、payload和signature[^1]。 下面是在Gin框架使用JWT的示例代码[^2]: 1. 导入所需的包: ```go import ( "github.com/gin-gonic/gin" "github.com/dgrijalva/jwt-go" ) ``` 2. 定义JWT的密钥: ```go var jwtKey = []byte("your_secret_key") ``` 3. 创建一个JWT的Claims结构体,用于存储用户的信息: ```go type Claims struct { Username string `json:"username"` jwt.StandardClaims } ``` 4. 创建一个处理登录请求的路由: ```go func login(c *gin.Context) { var loginData LoginData if err := c.ShouldBindJSON(&loginData); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"}) return } // 验证用户名和密码 if loginData.Username == "admin" && loginData.Password == "password" { // 创建JWT的Claims claims := &Claims{ Username: loginData.Username, StandardClaims: jwt.StandardClaims{ ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), // 设置过期时间 }, } // 创建JWT token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString(jwtKey) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"}) return } // 返回JWT给客户端 c.JSON(http.StatusOK, gin.H{"token": tokenString}) } else { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"}) } } ``` 5. 创建一个需要身份验证的路由: ```go func protectedRoute(c *gin.Context) { // 从请求头中获取JWT authHeader := c.GetHeader("Authorization") if authHeader == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Missing authorization header"}) return } // 解析JWT tokenString := authHeader[7:] // 去除Bearer前缀 claims := &Claims{} token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { return jwtKey, nil }) if err != nil { if err == jwt.ErrSignatureInvalid { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token signature"}) return } c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid token"}) return } if !token.Valid { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"}) return } // 验证通过,继续处理请求 c.JSON(http.StatusOK, gin.H{"message": "Protected route"}) } ``` 6. 在路由中注册处理函数: ```go func main() { r := gin.Default() r.POST("/login", login) r.GET("/protected", protectedRoute) r.Run(":8080") } ``` 以上代码演示了在Gin框架使用JWT进行身份验证和授权的基本流程。用户可以通过发送登录请求获取JWT,然后在需要身份验证的路由中将JWT放入请求头中进行验证。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

都学点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值