第22章 go的web框架-gin
目录
1 gin的helloworld体验
2 使用New和Default初始化路由器的区别
new 没有错误捕捉
3 gin的路由分组
4 获取url中的变量
5 获取get和post表单信息
查看代码
6 gin返回protobuf
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 的尾部
改成 abort index
移到最大的数
查看代码
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的优雅退出
查看代码