第22章 go的web框架-gin

1 gin的helloworld体验

2 使用New和Default初始化路由器的区别

new 没有错误捕捉

3 gin的路由分组

4 获取url中的变量

5 获取get和post表单信息

查看代码

6 gin返回protobuf

img

z_ch22/gin_start/ch06/main.go

查看代码
package main

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

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

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

	router.Run(":8083")
}

func returnProto(c *gin.Context) {
	course := []string{"python", "go", "微服务"}
	user := &proto.Teacher{
		Name:   "lele",
		Course: course,
	}
	c.ProtoBuf(http.StatusOK, user)
}

func moreJSON(c *gin.Context) {
	var msg struct {
		Name    string `json:"user"`
		Message string
		Number  int
	}
	msg.Name = "bobby"
	msg.Message = "这是一个测试json"
	msg.Number = 20

	c.JSON(http.StatusOK, msg)
}

z_ch22/gin_start/ch06/proto/user.proto

查看代码
syntax = "proto3";
option go_package = "./;proto"; // 要指明路径为当前的相对路径,不然会报错
message Teacher{
  string name = 1;
  repeated string course = 2;
}

然后在文件目录同级的命令行执行下面的命令:

protoc -I . user.proto --go_out=plugins=grpc:. 会生成 user.pb.go

拷贝相同的user.proto 文件到python的目录下面,

生成pyhton的proto文件

先安确保安装以下包

pip install grpcio
pip install grpcio-tools

然后在同级目录的命令行执行

python -m grpc_tools.protoc -I=./ --python_out=. --grpc_python_out=. ./user.proto

会生成 user_pb2.py 、user_pb2_grpc.py 两个文件

编写python请求

查看代码
import requests

import user_pb2

user = user_pb2.Teacher()

rsp = requests.get("http://127.0.0.1:8083/someProtoBuf")

user.ParseFromString(rsp.content)
print(user.name, user.course)

结果执行结果: lele ['python', 'go', '微服务']

7 登录的表单验证

https://pkg.go.dev/github.com/go-playground/validator/v10#section-readme

8 注册表单的验证

9 表单验证错误翻译成中文

10 表单中文翻译的json格式化细节

查看代码
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"
)

var trans ut.Translator

type LoginForm struct {
	User     string `json:"user" binding:"required,min=3,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:"re_password" binding:"required,eqfield=Password"` //跨字段
}

func InitTrans(locale string) (err error) {
	//修改gin框架中的validator引擎属性, 实现定制
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		//注册一个获取json的tag的自定义方法
		v.RegisterTagNameFunc(func(field reflect.StructField) string {
			fmt.Println("---", field.Tag.Get("json"))
			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
	}
	return
}

func removeTopStruct(fileds map[string]string) map[string]string {
	fmt.Println("1111", fileds)
	rsp := map[string]string{}
	for filed, err := range fileds {
		//fmt.Println(filed, err)
		// 截取LoginForm.user  '.' 后的字段
		rsp[filed[strings.Index(filed, ".")+1:]] = err
	}
	fmt.Println("222222", rsp)
	return rsp
}

func main() {
	//代码侵入性很强 中间件
	if err := InitTrans("zh"); err != nil {
		fmt.Println("初始化翻译器错误")
		return
	}

	router := gin.Default()
	router.POST("/loginJSON", func(c *gin.Context) {
		var loginForm LoginForm
		if err := c.ShouldBind(&loginForm); err != nil {
			fmt.Println("LoginForm err", err)
			errs, ok := err.(validator.ValidationErrors)

			fmt.Println("-----", errs, ok)
			if !ok { // 如果没有捕捉到
				c.JSON(http.StatusOK, gin.H{
					"msg": err.Error(),
				})
			}

			c.JSON(http.StatusBadRequest, gin.H{

				"error": removeTopStruct(errs.Translate(trans)),
			})
			return

		}

		c.JSON(http.StatusOK, gin.H{
			"msg": "登录成功",
		})

	})

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

	_ = router.Run(":8083")
}

11 自定义gin中间件

12 通过abort终止中间件后续逻辑的执行

13 gin的中间件原理源码分析

注册的中间件添加到了handler 的尾部

image

image

image

image

改成 abort index
image

移到最大的数
image

查看代码
package main

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

func MyLogger() gin.HandlerFunc {
	return func(c *gin.Context) {
		t := time.Now()
		c.Set("example", "123456")
		//让原本改执行的逻辑继续执行
		c.Next()

		end := time.Since(t)
		fmt.Printf("耗时:%V\n", end)
		status := c.Writer.Status()
		fmt.Println("状态", status)
	}
}

func TokenRequired() gin.HandlerFunc {
	return func(c *gin.Context) {
		var token string
		fmt.Println("headers", c.Request.Header)
		for k, v := range c.Request.Header {

			if k == "X-Token" {
				token = v[0]
			} else {
				fmt.Println(k, v)
			}
		}

		if token != "lele" {
			c.JSON(http.StatusUnauthorized, gin.H{
				"msg": "未登录",
			})
			//挑战 为什么连return都阻止不了后续逻辑的执行
			c.Abort()
		}
		c.Next()
	}
}

func main() {
	router := gin.Default()
	//使用logger和recovery中间件 全局所有
	router.Use(TokenRequired())

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

	router.Run(":8083")
}

请求测试

查看代码
import requests

rsp = requests.get("http://127.0.0.1:8083/ping", headers={
    "x-token":"lele"
})
print(rsp.text)

14 gin返回html

15 加载多个html文件

16 static静态文件的处理

17 gin的优雅退出

点击查看代码地址

codeup 地址

查看代码

posted @ 2023-01-26 09:13  元贞  阅读(31)  评论(0编辑  收藏  举报