当前位置: 首页 > news >正文

gin学习

gin学习笔记,不仅包含了基本的增删查改外,还包括参数传递,上传下载,模版、session与中间件等,方便收藏自习可用

文章目录

  • 获得个请求
  • get打印字符串
  • get请求xml
  • get请求跳转
  • http方法
  • 路由
  • 可以通过Context的Param方法来获取API参数
  • 可以通过DefaultQuery()或Query()方法获取
  • 可以通过GetQuery()与Query()方法获取
  • 可以通过BindQuery
  • 可以通过shouldBindQuery()传递
  • QueryMap进行参数传递
  • 结构体有map进行传递
  • 表单参数传递
  • 路径参数传递
  • 上传一个文件
    • 上传特定文件
    • 上传多个文件
  • 上传,对文件进行保存
    • routes group
    • 404 not found
    • json数据解析和绑定
    • 表单数据解析和绑定
    • url数据绑定
    • 各种数据格式的响应
  • html模板渲染
  • 静态资源文件进行渲染
  • 模版取消转义
  • go重定向
  • cookie
  • 多session
  • redis Session固化存储
  • 异步执行与同步执行
  • 中间件
  • 全局中间件
  • 局部中间件
    • 定义两个路由,计算执行时间
    • cookie的使用
    • Cookie练习
    • 结构体验证
    • 结构体

获得个请求

package mainimport "github.com/gin-gonic/gin"func main() {r := gin.Default()//r.Get("/hello")r.GET("/", func(c *gin.Context) {c.JSON(200, gin.H{"message": "hello world",})})err := r.Run(":8080")if err != nil {panic(err)}
}

get打印字符串

type User struct {Id         int64             `form:"id" json:"id" uri:"id"`Name       string            `form:"name" json:"name" uri:"name"`Address    []string          `form:"address" binding:"required" json:"address" uri:"address"`AddressMap map[string]string `form:"addressMap" json:"addressMap" uri:"addressMap"`
}func main() {r := gin.Default()r.GET("/user/save", func(c *gin.Context) {c.String(http.StatusOK, "this is %s", "ms string value")})r.Run(":8081")
}

get请求xml

type User struct {Id         int64             `form:"id" json:"id" uri:"id" xml:"id"`Name       string            `form:"name" json:"name" uri:"name" xml:"name"`Address    []string          `form:"address" binding:"required" json:"address" uri:"address" xml:"address"`AddressMap map[string]string `form:"addressMap" json:"addressMap" uri:"addressMap" xml:"addressMap"`
}func main() {r := gin.Default()r.GET("/user/save", func(c *gin.Context) {id := c.Query("id")name := c.Query("name")c.XML(http.StatusOK, gin.H{"id": id, "message": name})})r.Run(":8081")
}

get请求跳转

http方法

  • GET :get方法请求一个指定资源的表示形式,或使用GET请求应该只被用于获取数据
  • POST :POST方法用于将实体提交到指定资源,通常会导致在服务器上的状态变化
  • HEAD:HEAD方法请求一个与GET请求的响应相同的响应,但没有响应体
  • PUT:PUT方法用请求有效载荷替换目标资源的所有当前表示
  • DELETE : DELETE 方法删除指定的资源

路由

func main() {r := gin.Default()//r.Get("/hello")r.GET("/", func(c *gin.Context) {c.JSON(200, gin.H{"message": "hello world",})})r.GET("/get", func(c *gin.Context) {c.JSON(200, gin.H{"message": "hello world",})})r.POST("/save", func(c *gin.Context) {c.JSON(200, gin.H{"message": "save",})})r.PUT("/put", func(c *gin.Context) {c.JSON(200, gin.H{"message": "put",})})r.DELETE("/delete", func(c *gin.Context) {c.JSON(200, gin.H{"message": "hello DELETE",})})r.Any("/Any", func(c *gin.Context) {c.JSON(200, gin.H{"message": "hello Any",})})r.GET("/user/find/:id", func(ctx *gin.Context) {ctx.JSON(200, ctx.Param("id"))})r.GET("/u/*Path", func(ctx *gin.Context) {ctx.JSON(200, ctx.Param("Path"))})v1 := r.Group("/v1"){v1.GET("find", func(ctx *gin.Context) {ctx.JSON(200, gin.H{"message": "find",})})}v2 := r.Group("/v2"){v2.GET("find", func(ctx *gin.Context) {ctx.JSON(200, gin.H{"message": "find",})})}err := r.Run(":8080")if err != nil {panic(err)}
}

可以通过Context的Param方法来获取API参数

func main() {r := gin.Default()r.GET("/user/:name/*action", func(c *gin.Context) {name := c.Param("name")action := c.Param("action")action = strings.Trim(action, "/")c.String(http.StatusOK, name+"is "+action)})//r.POST("/xxxpost", getting)r.Run(":8000")
}

可以通过DefaultQuery()或Query()方法获取

传个localhost:8080/user?name=zhangsan

func main() {r := gin.Default()r.GET("/user", func(c *gin.Context) {name := c.DefaultQuery("name", "枯藤")c.String(http.StatusOK, "Hello %s", name)})r.Run(":8080")
}

可以通过GetQuery()与Query()方法获取

func main() {r := gin.Default()//r.Get("/hello")r.GET("/user/save", func(c *gin.Context) {id := c.Query("id")name := c.Query("name")addr, ok := c.GetQuery("addr")c.JSON(200, gin.H{"id":   id,"name": name,"addr": addr,"errs": ok,})})err := r.Run(":8080")if err != nil {panic(err)}
}

可以通过BindQuery

type User struct {Id   int64  `form:"id"`Name string `form:"name"`
}func main() {r := gin.Default()//r.Get("/hello")r.GET("/user/save", func(c *gin.Context) {var user Usererr := c.BindQuery(&user)if err != nil {log.Fatalln(err)}c.JSON(http.StatusOK, user)})errs := r.Run(":8080")if errs != nil {panic(errs)}
}

可以通过shouldBindQuery()传递

type User struct {Id      int64    `form:"id"`Name    string   `form:"name"`Address []string `form:"address" binding:"required"`
}func main() {r := gin.Default()//r.Get("/hello")r.GET("/user/save", func(c *gin.Context) {var user Userc.ShouldBindQuery(&user)c.JSON(http.StatusOK, user)})errs := r.Run(":8080")if errs != nil {panic(errs)}
}

QueryMap进行参数传递

type User struct {Id      int64    `form:"id"`Name    string   `form:"name"`Address []string `form:"address" binding:"required"`
}func main() {r := gin.Default()//r.Get("/hello")r.GET("/user/save", func(c *gin.Context) {addressMap := c.QueryMap("addressMap")c.JSON(200, addressMap)})errs := r.Run(":8080")if errs != nil {panic(errs)}
}
localhost:8080/user/save?addressMap[c]=d&addressMap[d]=a
{"c": "d","d": "a"
}

结构体有map进行传递

type User struct {Id         int64             `form:"id"`Name       string            `form:"name"`Address    []string          `form:"address" binding:"required"`AddressMap map[string]string `form:"addressMap"`
}func main() {r := gin.Default()//r.Get("/hello")r.GET("/user/save", func(c *gin.Context) {var user Userc.ShouldBind(&user)user.AddressMap = c.QueryMap("addressMap")c.JSON(200, user)})_ = r.Run(":8080")}

表单参数传递

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head>
<body>
<form action="http://localhost:8080/form" method="post" action="application/x-www-form-urlencoded">用户名:<input type="text" name="username" placeholder="请输入你的用户
名">  <br>&nbsp;&nbsp;&nbsp;码:<input type="password" name="userpassword" placeholder="请输入你的密码">  <br><input type="submit" value="提交">
</form>
</body>
</html>

用一个表单进行参数传递

func main() {r := gin.Default()r.POST("/form", func(c *gin.Context) {types := c.DefaultPostForm("type", "post")username := c.PostForm("username")password := c.PostForm("userpassword")c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))})r.Run(":8080")
}

关于表单的其他参数

type User struct {Id         int64             `form:"id"`Name       string            `form:"name"`Address    []string          `form:"address" binding:"required"`AddressMap map[string]string `form:"addressMap"`
}func main() {r := gin.Default()//r.Get("/hello")r.POST("/user/save", func(c *gin.Context) {id := c.PostForm("id")name := c.PostForm("name")address := c.PostFormArray("address")addressMap := c.PostFormMap("addressMap")c.JSON(200, gin.H{"code":         200,"msg":          "success","id":           id,"name":         name,"addressMap":   addressMap,"addressSlice": address,})})r.Run(":8081")}

也可以用绑定获取表单传过来的参数

type User struct {Id         int64             `form:"id"`Name       string            `form:"name"`Address    []string          `form:"address" binding:"required"`AddressMap map[string]string `form:"addressMap"`
}func main() {r := gin.Default()//r.Get("/hello")r.POST("/user/save", func(c *gin.Context) {var user Userc.ShouldBind(&user)addressMap := c.PostFormMap("addressMap")user.AddressMap = addressMapc.JSON(200, user)})r.Run(":8081")}

也可以用json形式

type User struct {Id         int64             `form:"id" json:"id"`Name       string            `form:"name" json:"name"`Address    []string          `form:"address" binding:"required" json:"address"`AddressMap map[string]string `form:"addressMap" json:"addressMap"`
}func main() {r := gin.Default()r.POST("/user/save", func(c *gin.Context) {var user Userc.ShouldBindJSON(&user)c.JSON(200, user)})r.Run(":8081")
}

路径参数传递

在这里插入代码片

上传一个文件

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head>
<body>
<form action="http://localhost:8080/upload" method="POST" enctype="multipart/form-data"><input type="file" name="file" /><button type="submit">上传</button>
</form>
</body>
</html>

go代码


func main() {r := gin.Default()r.MaxMultipartMemory = 8 << 20 // 8 MBr.POST("/upload", func(c *gin.Context) {file, err := c.FormFile("file")if err != nil {log.Printf("上传文件时发生错误: %v", err)c.String(500, "上传图片出错: %v", err)return}// 检查文件类型if !strings.HasPrefix(file.Header.Get("Content-Type"), "image/") {c.String(400, "只允许上传图片文件")return}// 确保上传目录存在if err := os.MkdirAll("./uploads", os.ModePerm); err != nil {log.Printf("创建文件夹失败: %v", err)c.String(500, "创建文件夹失败: %v", err)return}// 保存文件savePath := "./uploads/" + file.Filenameif err := c.SaveUploadedFile(file, savePath); err != nil {log.Printf("保存文件时发生错误: %v", err)c.String(500, "保存文件出错: %v", err)return}c.String(http.StatusOK, "文件上传成功: %s", file.Filename)})r.Run(":8080")
}

上传特定文件

func main() {r := gin.Default()r.POST("/upload", func(c *gin.Context) {_, headers, err := c.Request.FormFile("file")if err != nil {log.Printf("Error when try yo get file:%v", err)}if headers.Size > 1024*1024*2 {fmt.Println("文件太大了...")return}if headers.Header.Get("Content-Type") != "image/png" {fmt.Println("只允许上传png图片")return}c.SaveUploadedFile(headers, "./video/"+headers.Filename)c.String(http.StatusOK, headers.Filename)})r.Run(":8080")
}

上传多个文件

func main() {r := gin.Default()r.MaxMultipartMemory = 8 << 20r.POST("/upload", func(c *gin.Context) {form, err := c.MultipartForm()if err != nil {c.String(http.StatusBadRequest, fmt.Sprintf("get error: %s", err.Error()))}//获取所有图片files := form.File["files"]//遍历所有图片for _, file := range files {if err := c.SaveUploadedFile(file, file.Filename); err != nil {c.String(http.StatusBadRequest, fmt.Sprintf("upload error:%s", err.Error()))return}}c.String(200, fmt.Sprintf("upload ok %d files", len(files)))})r.Run(":8000")
}

上传,对文件进行保存

type User struct {Id         int64             `form:"id" json:"id" uri:"id"`Name       string            `form:"name" json:"name" uri:"name"`Address    []string          `form:"address" binding:"required" json:"address" uri:"address"`AddressMap map[string]string `form:"addressMap" json:"addressMap" uri:"addressMap"`
}func main() {r := gin.Default()r.POST("/user/save/:id/:name", func(c *gin.Context) {form, err := c.MultipartForm()if err != nil {panic(err)}value := form.Valuefiles := form.Filefor _, fileArray := range files {for _, f := range fileArray {c.SaveUploadedFile(f, "./"+f.Filename)}}c.JSON(200, value)})r.Run(":8081")
}

routes group

路由组合

func main() {r := gin.Default()v1 := r.Group("/v1"){v1.GET("/login", login)v1.GET("submit", submit)}v2 := r.Group("/v2"){v2.POST("/login", login)v2.POST("/submit", submit)}r.Run(":8080")
}func login(c *gin.Context) {name := c.DefaultQuery("name", "jack")c.String(200, fmt.Sprintf("hello %s %s\n", name, name))
}func submit(c *gin.Context) {name := c.DefaultQuery("name", "lily")c.String(200, fmt.Sprintf("hello %s %s\n", name, name))
}

404 not found

func main() {r := gin.Default()r.GET("/user", func(c *gin.Context) {name := c.DefaultQuery("name", "枯藤")c.String(http.StatusOK, fmt.Sprintf("hello %s", name))})r.NoRoute(func(c *gin.Context) {c.String(http.StatusNotFound, "404 page not found222222222222")})r.Run(":8080")}

json数据解析和绑定


type Login struct {User     string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`Password string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}func main() {//创建路由r := gin.Default()//json绑定r.POST("loginJSON", func(c *gin.Context) {var json Loginif err := c.ShouldBindJSON(&json); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if json.User != "root" || json.Password != "123456" {c.JSON(http.StatusBadRequest, gin.H{"status": "304"})return}c.JSON(http.StatusOK, gin.H{"status": "200"})})r.Run(":8080")
}

表单数据解析和绑定


type Login struct {User     string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`Password string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}func main() {//创建路由r := gin.Default()r.POST("/loginForm", func(c *gin.Context) {var form Loginif err := c.Bind(&form); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if form.User != "root" || form.Password != "admin" {c.JSON(http.StatusBadRequest, gin.H{"error": "304"})return}c.JSON(http.StatusOK, gin.H{"status": "200"})})r.Run(":8080")}

url数据绑定

type Login struct {User     string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`Password string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}func main() {r := gin.Default()//json绑定r.GET("/:user/:password", func(c *gin.Context) {var login Loginif err := c.ShouldBindUri(&login); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}//判断用户名密码是否正确if login.User != "root" || login.Password != "123456" {c.JSON(http.StatusBadRequest, gin.H{"error": "304"})return}c.JSON(http.StatusOK, gin.H{"message": "200"})})r.Run(":8080")}

各种数据格式的响应

func main() {//创建路由r := gin.Default()//1.jsonr.GET("/someJSON", func(c *gin.Context) {c.JSON(200, gin.H{"message": "someJSON", "status": 200})})//结构体响应r.GET("/someStruct", func(c *gin.Context) {var msg struct {Name    stringMessage stringNumber  int}msg.Name = "root"msg.Message = "message"msg.Number = 124c.JSON(200, msg)})r.GET("/someXML", func(c *gin.Context) {c.XML(200, gin.H{"message": "abc"})})//yaml响应r.GET("/someYAML", func(c *gin.Context) {c.YAML(200, gin.H{"message": "zhangsan"})})//5. protobuf 格式r.GET("/someProtobuf", func(c *gin.Context) {reps := []int64{int64(1), int64(2)}label := "label"data := &protoexample.Test{Label: &label,Reps:  reps,}c.ProtoBuf(200, data)})r.Run(":8080")
}

html模板渲染

目录:
在这里插入图片描述

func main() {//创建路由r := gin.Default()r.LoadHTMLGlob("tem/*")r.GET("/", func(c *gin.Context) {c.HTML(http.StatusOK, "index.html", gin.H{"title": "我是测试","ce":    "123456",})})r.Run(":8080")}

在这里插入图片描述

{{ define "user/index.html" }}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>{{.title}}</title>
</head>
<body>
fgkjdskjdsh{{.address}}
</body>
</html>
{{ end }}
func main() {//创建路由r := gin.Default()r.LoadHTMLGlob("tem/**/*")r.GET("/index", func(c *gin.Context) {c.HTML(http.StatusOK, "user/index.html", gin.H{"title":   "我是测试","address": "www.61mh.com",})})r.Run(":8080")}

模板嵌套
header.html

{{define "public/header"}}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{{.title}}</title>
</head>
<body>{{end}}

footer.html

{{define "public/footer"}}
</body>
</html>
{{end}}

index.html

{{ define "user/index.html" }}
{{template "public/header" .}}
fgkjdskjdsh{{.address}}
{{template "public/footer" .}}
{{ end }}
func main() {//创建路由r := gin.Default()r.LoadHTMLGlob("tem/**/*")r.GET("/index", func(c *gin.Context) {c.HTML(http.StatusOK, "user/index.html", gin.H{"title":   "Goland Projects","address": "www.51mh.com",})})r.Run(":8080")}

静态资源文件进行渲染


func main() {r := gin.Default()//r.LoadHTMLFiles("./templates/index.tmpl", "./templates/user.tmpl")r.SetFuncMap(template.FuncMap{"safe": func(str string) template.HTML {return template.HTML(str)},})r.Static("/css", "./templates/css")r.LoadHTMLGlob("templates/**/*")r.GET("/index", func(c *gin.Context) {c.HTML(http.StatusOK, "index.tmpl", gin.H{"title": "<a href='http://www.baidu.com'>hello template</a>",})})r.GET("/user", func(c *gin.Context) {c.HTML(http.StatusOK, "user.tmpl", gin.H{"title": "<a href='http://www.baidu.com'>hello template</a>",})})r.Run(":8081")
}

模版取消转义

package mainimport ("github.com/gin-gonic/gin""html/template""net/http"
)func main() {r := gin.Default()//r.LoadHTMLFiles("./templates/index.tmpl", "./templates/user.tmpl")r.SetFuncMap(template.FuncMap{"safe": func(str string) template.HTML {return template.HTML(str)},})r.LoadHTMLGlob("templates/**/*")r.GET("/index", func(c *gin.Context) {c.HTML(http.StatusOK, "index.tmpl", gin.H{"title": "<a href='http://www.baidu.com'>hello template</a>",})})r.GET("/user", func(c *gin.Context) {c.HTML(http.StatusOK, "user.tmpl", gin.H{"title": "<a href='http://www.baidu.com'>hello template</a>",})})r.Run(":8081")
}

go重定向

func main() {//创建路由r := gin.Default()r.GET("/index", func(c *gin.Context) {c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")})r.Run(":8080")
}

cookie

设置cookie

c.SetCookie("site_cookie", "123456", 60*60*24, "/", "", false, false)

删除cookie

c.SetCookie("site_cookie", "123456", -1, "/", "", false, false)

读取cookie

data, err := c.Cookie("site_cookie")if err != nil {log.Println(err)c.JSON(200, err.Error())return}c.JSON(200, data)

多session

func main() {r := gin.Default()store := cookie.NewStore([]byte("secret"))sessionNames := []string{"a", "b"}r.Use(sessions.SessionsMany(sessionNames, store))r.GET("/hello", func(c *gin.Context) {sessionA := sessions.DefaultMany(c, "a")sessionB := sessions.DefaultMany(c, "b")if sessionA.Get("name") == nil {sessionA.Set("name", "b")sessionA.Save()}if sessionB.Get("name") == nil {sessionB.Set("name", "c")sessionB.Save()}})r.Run(":8081")
}

redis Session固化存储

func main() {r := gin.Default()store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("secret"))r.Use(sessions.Sessions("mysession", store))r.GET("/incr", func(c *gin.Context) {session := sessions.Default(c)var cnt intv := session.Get("count")if v == nil {cnt = 0} else {cnt = v.(int)cnt++}session.Set("count", cnt)session.Save()c.JSON(200, gin.H{"count": cnt,})})r.Run(":8080")

异步执行与同步执行

func main() {//创建路由r := gin.Default()//异步r.GET("/long_async", func(c *gin.Context) {//一个副本copyContext := c.Copy()go func() {time.Sleep(3 * time.Second)log.Println("异步执行:" + copyContext.Request.URL.Path)}()})//同步r.GET("/long_sync", func(c *gin.Context) {time.Sleep(3 * time.Second)log.Println("同步执行:" + c.Request.URL.Path)})r.Run(":8080")
}

中间件

在gin框架中,中间件是指可以拦截http请求-响应生命周期的特殊函数,在请求-响应生命周期中可以注册多个中间件,每个中间件执行不同的功能,一个中间件执行完再轮到下一个中间件执行。

全局中间件

func MiddleWare() gin.HandlerFunc {return func(c *gin.Context) {t := time.Now()fmt.Println("中间件开始执行了")c.Set("request", "中间件")status := c.Writer.Status()fmt.Println("中间件执行完毕", status)t2 := time.Since(t)fmt.Println("time:", t2)}
}func main() {r := gin.Default()//注册中间价r.Use(MiddleWare())//{}代码规范{r.GET("/ce", func(c *gin.Context) {req, _ := c.Get("request")fmt.Println("request:", req)c.JSON(200, gin.H{"request": req})})}r.Run(":8080")
}

局部中间件


func MiddleWare() gin.HandlerFunc {return func(c *gin.Context) {t := time.Now()fmt.Println("中间开始执行了")c.Set("request", "中间剑")c.Next()status := c.Writer.Status()fmt.Println("中间件执行完毕", status)t2 := time.Since(t)fmt.Println("time:", t2)}
}func main() {r := gin.Default()r.Use(MiddleWare()){r.GET("/ce", func(c *gin.Context) {req, _ := c.Get("request")fmt.Println("request:", req)c.JSON(200, gin.H{"request": req})})}r.Run(":8080")
}

定义两个路由,计算执行时间


func myTime(c *gin.Context) {start := time.Now()c.Next()since := time.Since(start)fmt.Println("程序用时:", since)
}func main() {r := gin.Default()//注册中间件r.Use(myTime)shoppingGroup := r.Group("/shopping"){shoppingGroup.GET("/index", shopIndexHandler)shoppingGroup.GET("/home", shopHomeHandler)}r.Run(":8080")}func shopIndexHandler(c *gin.Context) {time.Sleep(5 * time.Second)
}func shopHomeHandler(c *gin.Context) {time.Sleep(3 * time.Second)fmt.Println("how do you do?")
}

cookie的使用

func main() {r := gin.Default()r.GET("/cookie", func(c *gin.Context) {cookie, err := c.Cookie("key_code")if err != nil {cookie = "ThisIsCookie"c.SetCookie("key_code", "value_cookie", 60, "/","localhost", false, true)}fmt.Printf("cookie的值是:%s\n", cookie)})r.Run(":8080")
}

Cookie练习

func AuthMiddleWare() gin.HandlerFunc {return func(c *gin.Context) {if cookie, err := c.Request.Cookie("username"); err == nil {if cookie.String() == "123" {c.Next()return}}c.JSON(http.StatusUnauthorized, gin.H{"error": "error"})c.Abort()return}
}
func main() {r := gin.Default()r.GET("/login", func(c *gin.Context) {c.SetCookie("abc", "123", 60, "/", "localhost", false, true)c.String(200, "Login Success!")})r.GET("/home", AuthMiddleWare(), func(c *gin.Context) {c.JSON(200, gin.H{"data": "home"})})r.Run(":8080")
}

结构体验证

type Person struct {Age      int       `form:"age" binding:"required,gt=10"`Name     string    `form:"name" binding:"required"`Birthday time.Time `form:"birthday" time_format:"2006-01-02"` time_utc: "1"
}func main() {r := gin.Default()r.GET("/51mh", func(c *gin.Context) {var person Personif err := c.ShouldBind(&person); err != nil {c.String(500, fmt.Sprint(err))return}c.String(200, fmt.Sprintf("%#v", person))})r.Run(":8080")
}

结构体


type Login struct {User     string `uri:"user" validate:"checkName"`Password string `uri:"password"`
}func checkName(f1 validator.FieldLevel) bool {if f1.Field().String() != "root" {return false}return true
}func main() {r := gin.Default()validate := validator.New()r.GET("/:user/:password", func(c *gin.Context) {var login Loginerr := validate.RegisterValidation("checkName", checkName)if err := c.ShouldBindUri(&login); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}err = validate.Struct(login)if err != nil {for _, err := range err.(validator.ValidationErrors) {fmt.Println(err)}return}fmt.Println("success")})r.Run(":8080")}

相关文章:

gin学习

gin学习笔记&#xff0c;不仅包含了基本的增删查改外&#xff0c;还包括参数传递&#xff0c;上传下载&#xff0c;模版、session与中间件等&#xff0c;方便收藏自习可用 文章目录 获得个请求get打印字符串get请求xmlget请求跳转http方法路由可以通过Context的Param方法来获取…...

【HarmonyOS NEXT】关键资产存储开发案例

在 iOS 开发中 Keychain 是一个非常安全的存储系统&#xff0c;用于保存敏感信息&#xff0c;如密码、证书、密钥等。与文件系统不同&#xff0c;Keychain 提供了更高的安全性&#xff0c;因为它对数据进行了加密&#xff0c;并且只有经过授权的应用程序才能访问存储的数据。那…...

高德终端技术总结:高可用架构如何练成?

前言 高德地图作为国民级应用&#xff0c;特别是出行场景的独特性&#xff0c;要确保在线导航高并发和交通安全级的超稳定性&#xff0c;这对技术团队提出异乎寻常的高要求&#xff0c;无论是终端、云端&#xff0c;还是“终端-云端”之间的连接&#xff0c;都必须实现“高可用…...

STM32八股【3】------RAM和片上FLASH

1、RAM和FLASH构成 1.RAM ┌──────────────────────────┐ │ 栈区 (Stack) │ ← 从RAM顶端向下扩展&#xff08;存储局部变量、函数调用信息&#xff09; │--------------------------│ │ 堆区 (Heap) │ ← …...

Apache Doris

Apache Doris介绍 Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人们所熟知&#xff0c;仅需亚秒级响应时间即可返回海量数据下的查询结果&#xff0c;不仅可以支持高并发的点查询场景&#xff0c;也能支持高吞吐的复杂分析场…...

Debezium介绍

1.什么是Debezium Debezium 是一个开源的分布式平台&#xff0c;用于捕获数据库的变更事件&#xff08;CDC&#xff0c;Change Data Capture&#xff09;。它能够实时捕获数据库中的行级更改&#xff0c;并将这些更改作为事件流发送到消息中间件&#xff08;如 Apache Kafka&a…...

奇迹科技:蓝牙网关赋能少儿篮球教育的创新融合案例研究

一、引言 本文研究了福建奇迹运动体育科技有限公司&#xff08;简称‘奇迹科技’&#xff09;如何利用其创新产品体系和桂花网蓝牙网关M1500&#xff0c;与少儿篮球教育实现深度融合。重点分析其在提升教学效果、保障训练安全、优化个性化教学等方面的实践与成效&#xff0c;为…...

Python散点图(Scatter Plot):高阶分析、散点图矩阵、三维散点图及综合应用

散点图:数据分析的利器 在数据分析领域,散点图是一种直观且强大的可视化工具,广泛应用于揭示变量间的相关性以及识别数据集中的异常值。本文将深入探讨散点图的这两种关键功能,并结合实际案例与Python代码示例,带您全面了解散点图的应用。 一、散点图如何展示变量间的相…...

计算机网络层超全解析:从IP协议到路由算法

&#x1f310; &#xff08;专业详解生活化类比&#xff0c;逻辑一镜到底&#xff09; &#x1f4d6; 网络层的核心使命 核心任务&#xff1a;在不同网络间为数据包选择最佳路径&#xff0c;实现端到端通信。 类比&#xff1a;快递公司总部&#xff08;网络层&#xff09;根据…...

RoboVQA

RoboVQA:面向机器人技术的多模态长时推理 摘要 ​ 我们提出了一种可扩展、自下而上且具有内在多样性的数据收集方案,适用于中长时高级推理任务,其吞吐量比传统的自上而下分步收集方法高2.2倍。通过在3栋办公楼内使用多种实体(机器人、人类、使用抓取工具的人类)执行任意用…...

javascript语法入门

一、变量声明 在JavaScript中&#xff0c;可以使用var、let和const来声明变量。 javascript var name "张三"; let age 20; 二、数据类型 JavaScript中有7种基本数据类型&#xff1a;undefined、null、boolean、string、symbol、number&#xff0c;以及object。…...

前端字段名和后端不一致?解锁 JSON 映射的“隐藏规则” !!!

&#x1f680; 前端字段名和后端不一致&#xff1f;解锁 JSON 映射的“隐藏规则” &#x1f31f; 嘿&#xff0c;技术冒险家们&#xff01;&#x1f44b; 今天我们要聊一个开发中常见的“坑”&#xff1a;前端传来的 JSON 参数字段名和后端对象字段名不一致&#xff0c;会发生…...

Java——ArrayList集合

ArrayList&#xff1a;基于动态数组实现&#xff0c;支持随机访问&#xff0c;适合频繁的随机访问操作&#xff0c;但在插入和删除元素时性能较差。 技术层面介绍 所属类库&#xff1a;ArrayList 位于 java.util 包中&#xff0c;它实现了 List 接口&#xff0c;因此具备 Lis…...

基于python+django的图书借阅网站-图书借阅管理系统源码+运行步骤

该系统是基于pythondjango开发的在线图书借阅管理系统。系统适合场景&#xff1a;大学生、课程作业、系统设计、毕业设计。 演示地址 前台地址&#xff1a; http://book.gitapp.cn 后台地址&#xff1a;http://book.gitapp.cn/#/admin 后台管理帐号&#xff1a; 用户名&…...

Flutter运行错误:UG! exception in phase ‘semantic analysis‘

最近在Mac Mini M4上通过Android Studio导入Flutter项目并运行&#xff0c;结果一直跑不起来&#xff0c;错误日志如下&#xff1a; 执行命令查看版本信息&#xff1a; flutter doctor --verbose通过输出信息Java version OpenJDK Runtime Environment (build 21.0.41242208…...

Python-docx库详解:轻松实现Word文档自动化生成与图片尺寸控制

Python-docx库详解&#xff1a;轻松实现Word文档自动化生成与图片尺寸控制 在现代办公自动化的浪潮中&#xff0c;文档处理是一项不可或缺的任务。Python作为一种强大的编程语言&#xff0c;提供了丰富的库来简化这些任务。其中&#xff0c;python-docx库是处理Word文档的有力…...

【NLP 42、实践 ⑪ 用Bert模型结构实现自回归语言模型的训练】

目录 数据文件 一、模型定义 1.模型初始化 代码运行流程 2.前向传播&#xff0c;计算损失 ⭐ 代码运行流程 二、加载语料 代码运行流程 三、 随机生成样本 代码运行流程 四、建立模型 五、采样策略选择 代码运行流程 六、模型效果测试 代码运行流程 七、模型训练 代码运行流程 …...

HTTPS

目录 一 HTTPS是什么 二 加密 三 加密方案 四 CA机构/证书 五 最终方案(对称密钥/非对称密钥/CA证书)和总体流程 一 HTTPS是什么 在应用层存在SSL&#xff0c;TLS(HTTP之下&#xff0c;传输层之上)加密/解密安全协议&#xff0c;如果HTTP经过这个协议&#xff0c;对端也走…...

electron框架(4.0)electron-builde和electron Forge的打包方式

----使用electron-builder打包&#xff08;需要魔法&#xff09; --安装electron-builder: npm install electron-builder -D--package.json中进行相关配置&#xff1a; {"name": "video-tools","version": "1.0.0","main&quo…...

SaaS系统的销售微服务与权限微服务边界设计

在设计SaaS系统的销售微服务与权限微服务的边界时&#xff0c;需要结合领域驱动设计&#xff08;DDD&#xff09;和微服务拆分原则&#xff0c;确保高内聚、低耦合。以下是结合微服务架构原则、多租户SaaS需求及权限管理场景的完整设计方案&#xff0c;整合了权限服务与销售服务…...

Unity-AI-Deepseek生成的生成模型代码

结果 能用&#xff0c;不是很理想&#xff0c;从左到右&#xff0c;分别是body,眼睛,演睫毛,手指套&#xff08;如果你知道这是什么&#xff09;结果不是很理想 (下面代码已包含&#xff0c;修复的切线只能传Vector3参数&#xff0c;Unity2022测试&#xff09; 你们帮我看看…...

Django REST Framework 请求封装源码解析与实现流程

版本说明&#xff1a; Django: V4.2.20 Django Rest Framework: V3.15.2 一、核心封装流程示意图 #mermaid-svg-qXJLIa9Bx1TCiPSN {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-qXJLIa9Bx1TCiPSN .error-icon{fill…...

简介PyCDE:Python CIRCT Design Entry

简介PyCDE&#xff1a;Python CIRCT Design Entry 引言 在硬件设计和验证领域&#xff0c;随着设计复杂性的增加&#xff0c;传统的方法往往难以满足现代设计的需求。PyCDE&#xff08;Python CIRCT Design Entry&#xff09;作为CIRCT项目的一部分&#xff0c;旨在为硬件设计…...

Python实现deepseek接口的调用

简介&#xff1a;DeepSeek 是一个强大的大语言模型&#xff0c;提供 API 接口供开发者调用。在 Python 中&#xff0c;可以使用 requests 或 httpx 库向 DeepSeek API 发送请求&#xff0c;实现文本生成、代码补全&#xff0c;知识问答等功能。本文将介绍如何在 Python 中调用 …...

考研课程安排(自用)

文章目录 408数据结构&#xff08;王道&#xff09;计算机组成原理&#xff08;王道&#xff09;操作系统&#xff08;王道&#xff09;计算机网络&#xff08;湖科大版&#xff09; 数学一高等数学&#xff08;微积分&#xff09;线性代数和概率论 408 数据结构&#xff08;王…...

Mybatis操作数据库(注解+xml两个方式)

文章目录 1.个人回顾2.关于mybatis注解的说明3.字段和属性不匹配的解决方案3.1第一个方案3.2第二个方案3.3第三个方案 4.xml路径配置5.xml里面的字段映射 1.个人回顾 刚刚翻看了一下自己的这个之前写的博客&#xff0c;上一次和这个javaee相关的博客还是去年写的&#xff0c;也…...

文心快码 使用体验与介绍

一、文心快码的核心作用 文心快码是百度基于文心大模型开发的智能代码助手&#xff0c;核心价值包括&#xff1a; 代码生成与补全 通用开发&#xff1a;根据注释生成HTML/CSS/JS、Python、Java等代码C专项&#xff1a;生成类定义、STL容器操作、智能指针代码&#xff08;如st…...

区块链交易

文章目录 交易准备合约和代码逻辑合约compile.jsindex.js 运行 交易 项目来自https://github.com/Dapp-Learning-DAO/Dapp-Learning/blob/main/basic/02-web3js-transaction/README-cn.md 本项目包含对交易进行签名&#xff0c;发送&#xff0c;接收交易回执&#xff0c;验证…...

LeetCode 160 Intersection Of Two Linked Lists 相交链表 Java

题目&#xff1a;找到两个相交列表的起始点&#xff0c;如图c1开始为A和B两个链表的相交点 举例1&#xff1a;8为两个链表的相交点。 注意&#xff1a;相交不止是数值上的相同。 举例2&#xff1a;2为相交点 举例3&#xff1a;没有相交点 解题思路&#xff1a; 相交证明最后一…...

体育直播模板nba英超直播欧洲杯直播模板手机自适应

源码名称&#xff1a;体育直播模板nba英超直播欧洲杯直播模板手机自适应帝国cms 7.5模板 开发环境&#xff1a;帝国cms7.5 空间支持&#xff1a;phpmysql 带软件采集&#xff0c;可以挂着自动采集发布&#xff0c;无需人工操作&#xff01; 模板特点&#xff1a; 程序伪静态…...

Android Compose 图标按钮深度剖析:从源码到实践(五)

Android Compose 图标按钮深度剖析&#xff1a;从源码到实践 一、引言 在现代 Android 应用开发中&#xff0c;用户界面的交互性和美观性至关重要。图标按钮作为一种常见的 UI 元素&#xff0c;以其简洁直观的特点&#xff0c;在提升用户体验方面发挥着重要作用。Android Com…...

【Dive Into Stable Diffusion v3.5】2:Stable Diffusion v3.5原理介绍

【Dive Into Stable Diffusion v3.5】系列博文&#xff1a; 第1篇&#xff1a;开源项目正式发布——深入探索SDv3.5模型全参/LoRA/RLHF训练第2篇&#xff1a;Stable Diffusion v3.5原理介绍 目录 1 前言1.1 扩散模型的原理1.2 损失函数1.3 加噪流程1.4 推理流程1.5 negative pr…...

WPF 布局舍入(WPF 边框模糊 或 像素错位 的问题)

1. 什么是 WPF 布局舍入&#xff1f; 在 WPF 开发过程中&#xff0c;可能会遇到界面模糊、边框错位、文本渲染不清晰等问题。这些现象通常是由于 WPF 采用 设备无关像素&#xff08;DIP, Device Independent Pixels&#xff09;&#xff0c;在不同 DPI 设置下&#xff0c;UI 元…...

前端-选中pdf中的文字并使用,显示一个悬浮的翻译按钮(本地pdfjs+iframe)不适用textlayer

使用pdfjs移步– vue2使用pdfjs-dist实现pdf预览&#xff08;iframe形式&#xff0c;不修改pdfjs原来的ui和控件&#xff0c;dom层可以用display去掉一部分组件&#xff09; 方案1&#xff1a;获取选择文本内容的最前面的字符坐标的位置&#xff08;这种写法会导致如果选择超出…...

Mongodb分片模式部署

MongoDB 分片集群部署教程 1. 概述 MongoDB 分片是一种用于处理大规模数据集的集群技术。通过分片&#xff0c;MongoDB 可以将数据分布在多个服务器上&#xff0c;从而提高存储容量和读写性能。本教程将详细介绍如何从零开始部署 MongoDB 分片集群。 介绍 分片集群中主要由三…...

「一起学后端」Nest.js + MySQL 查询方法教学文档

Nest.js MySQL 查询方法教学文档 文章目录 Nest.js MySQL 查询方法教学文档1. 基础 CRUD 操作1.1 创建记录1.2 查询记录1.3 更新记录1.4 删除记录 2. 复杂查询2.1 分页与排序2.2 关联查询&#xff08;JOIN&#xff09;2.3 聚合查询2.4 子查询 3. 高级操作3.1 事务处理3.2 Typ…...

基于Spring Boot的企业内管信息化系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

【学习笔记】LLM推理与部署

文章目录 1 [LLMs inference] quantization 量化整体介绍&#xff08;bitsandbytes、GPTQ、GGUF、AWQ&#xff09;2 [LLMs inference] quantization 量化整体介绍&#xff08;bitsandbytes、GPTQ、GGUF、AWQ&#xff09;3 [LLMs serving] openrouter & vllm host LLM 推理服…...

leetcode-134.加油站

油箱容积无限&#xff0c;要求能环岛&#xff0c;说明总共加起来的油是不少于消耗量的&#xff0c;这是唯一一个决定能否环岛的变量&#xff0c;只要油量够&#xff0c;那么必定有一段路是攒油的路&#xff0c;它供给后续的消耗&#xff0c;使得油箱总是不空。 如果油量足够&am…...

companion object和object 从kotlin转java分析

说明 companion object 中 companion类中的方法是普通的方法 在外部类中生成静态变量&#xff0c;静态companion 对象 object MyClass2 中 类中方法是普通方法 在MyClass2中生成静态变量&#xff0c;静态MyClass2对象&#xff0c; 一 companion object 使用 kotlin转java pa…...

检索增强生成(2)本地PDF 本地嵌入模型

from langchain_community.document_loaders import PyPDFLoader from pathlib import Pathdef load_local_pdf(file_path):if not Path(file_path).exists():raise FileNotFoundError(f"文件 {file_path} 不存在&#xff01;")loader PyPDFLoader(file_path)try:do…...

关于TVS管漏电流的问题?

问题描述&#xff1a; 在量产的带电池故事机生产中&#xff0c;工厂产线测试电流时&#xff0c;有1台机器电流比正常机器大10mA左右。 原因分析&#xff1a; 1、分析电路原理图&#xff0c;去除可能出现问题的电压或器件&#xff08;不影响系统&#xff09;&#xff0c;发现…...

2025前端面试题记录

vue项目目录的执行顺序是怎么样的&#xff1f; 1、package.json   在执行npm run dev时&#xff0c;会在当前目录寻找package.json文件&#xff0c;此文件包含了项目的名称版本、项目依赖等相关信息。 2、webpack.config.js(会被vue-cli脚手架隐藏) 3、vue.config.js   对…...

Linux系统docker部署Ollama本地大模型及部署Hugging Face开源模型,ollama相关注意点,非ollama模型创建,模型量化,显存建议

本文主要描述在Linux系统使用docker部署ollama自有模型以及Hugging Face开源模型&#xff0c;也涉及到一些相关注意点&#xff0c;欢迎沟通讨论~ 拉取镜像 拉取ollama最新镜像&#xff1a;docker pull ollama/ollama:latest 运行ollama 执行&#xff1a;docker run -d --res…...

.NET 9 彻底改变了 API 文档:从 Swashbuckle(Swagger) 到 Scalar

示例代码下载&#xff1a;https://download.csdn.net/download/hefeng_aspnet/90404652 摘要 API 文档是现代软件开发的支柱。随着 .NET 9 从 Swashbuckle 转向 Microsoft.AspNetCore.OpenApi&#xff0c;开发人员需要新的策略来保持高效。本文探讨了这些变化&#xff0c;并介…...

C++——权限初识

权限初识 C中的访问权限主要分为三种&#xff1a; public 、 private 和 protected 。这些权限决定了类成员&#xff08;包括数据成员和成员函数&#xff09;的可访问性。以下是一个总结表格&#xff0c;说明了在不同情况下这些权限如何应用&#xff1a; 使用权限&#xff08;…...

如何让自动驾驶汽车“看清”世界?坐标映射与数据融合概述

在自动驾驶领域,多传感器融合技术是实现车辆环境感知和决策控制的关键。其中,坐标系映射和对应是多传感器融合的重要环节,它涉及到不同传感器数据在统一坐标系下的转换和匹配,以实现对车辆周围环境的准确感知。本文将介绍多传感器融合中坐标系映射和对应的数学基础和实际应…...

如何在 Linux 系统中部署 FTP 服务器:从基础配置到安全优化

一、为什么选择 Linux 部署 FTP 服务器&#xff1f; FTP&#xff08;文件传输协议&#xff09;作为互联网最早的文件传输标准&#xff0c;至今仍在企业内部文件共享、镜像站点同步等场景中广泛应用。Linux 系统凭借其稳定性、开源特性及丰富的工具链&#xff0c;成为搭建 FTP 服…...

C++——引用

目录 举个例子&#xff1a; 引用的基本特性 引用的定义语法如下&#xff1a; 1. 引用必须初始化&#xff1a; 2. 引用是别名&#xff1a; 3. 引用不能为空&#xff1a; 4. 引用不能重新绑定&#xff1a; 引用的使用场景 函数参数传递&#xff1a; 函数返回值&#xf…...

unity开发效率提升笔记

本文将记录提升Unity开发效率的若干细节&#xff0c;持续更新 一.VSCode文件标签多行显示 1.File->Preference->Settings (快捷键Ctrl 逗号) 2.搜索workbench.editor.wrapTabs 3.勾选上这个单选开关 若依然不是多行 4.搜索workbench.editor.tabSizing,选择fi…...