豆包MarsCode AI刷题

2 阅读12分钟

go进阶(1) gin框架


gin介绍

  • Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点;
  • 对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错;
  • 借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范;

gin安装

在go工作空间中创建新的目录,自定义名,使用

go mod init example/gin

对该包进行初始化;

创建一个go文件;

使用命令

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

在包中导入gin框架;

还可以导入

import "net/http"

方便使用HTTP状态码常量;

gin开始

在文件中写入

package main
​
import (
    "net/http"
​
    "github.com/gin-gonic/gin"
)
​
func main() {
    // 1.创建路由
   r := gin.Default()
   // 2.绑定路由规则,执行的函数
   // gin.Context,封装了request和response
   r.GET("/", func(c *gin.Context) {
      c.String(http.StatusOK, "hello World!")
   })
   // 3.监听端口,默认在8080
   // Run("里面不指定端口号默认为8080") 
   r.Run(":8000")
}

使用

go run .

启动项目后,在浏览器中输入http://localhost:8000,回车后界面上显示Hello World!并且命令行中输出

image-20240620174339021

则运行成功;

param配置

在地址中添加param,可以使用 : 与 ***** 对param进行标注

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.Run(":8000")
}

url地址中*param可以省略不写,但 :name 必须存在;

举例:http://localhost:8000/user/lxh可以访问,但http://localhost:8000/user不可以访问;

*param会携带 '/' ,需要使用strings.Trim()来去除字符串头尾的 '/';

query配置

在地址中添加query,若不添加可以使用DefaultQuery(key,defaultValue)来使用配置的默认值

func main() {
    r := gin.Default()
    r.GET("/user", func(c *gin.Context) {
        //指定默认值
        //http://localhost:8000/user 才会打印出来默认的值
        name := c.DefaultQuery("name", "枯藤")
        c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
    })
    r.Run(":8000")
}

若地址为http://localhost:8000/user?name=lxh

则输出hello lxh

若地址为http://localhost:8000/user

则输出hello 枯藤

表单配置

表单传输为post请求,http常见的传输格式为四种:

  • application/json
  • application/x-www-form-urlencoded
  • application/xml
  • multipart/form-data

表单参数可以通过PostForm()方法获取,该方法默认解析的是x-www-form-urlencoded或from-data格式的参数

对于前端代码为:

<!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:8000/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>

后端go代码为:

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(":8000")
}
​

则当前端跳转到http://localhost:8000/form提交数据则界面显示为:

userName:lxh,password:123456,type:post

可以使用原生的Request获得更详细的前端数据,如请求头、文件大小等;

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 to get file: %v", err)
        }
        //headers.Size 获取文件大小
        if headers.Size > 1024*1024 {
            fmt.Println("文件太大了")
            return
        }
        //headers.Header.Get("Content-Type")获取上传文件的类型
        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(":8000")
}

对于多文件获取:

func main() {
    r := gin.Default()  
    r.MaxMultipartMemory = 8 << 20
    r.POST("/upload", func(c *gin.Context) {
        form, err := c.MultipartForm()
        if err != nil {
            c.String(http.StatusBadRequest, fmt.Sprintf("get err %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 err %s", err.Error()))
                return
            }
        }
        c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
    })
    r.Run(":8000")
}

使用

c.MultipartForm()

来获得所有表单集合数据;

通过

form.File["files"]

来获取file类型的数据集合,对其遍历存储到本地中;

routes group

routes group是为了管理一些相同的URL;

func main() {
   // 1.创建路由
   // 默认使用了2个中间件Logger(), Recovery()
   r := gin.Default()
   // 路由组1 ,处理GET请求
   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(":8000")
}
​
func login(c *gin.Context) {
   name := c.DefaultQuery("name", "jack")
   c.String(200, fmt.Sprintf("hello %s\n", name))
}
​
func submit(c *gin.Context) {
   name := c.DefaultQuery("name", "lily")
   c.String(200, fmt.Sprintf("hello %s\n", name))
}

数据解析和绑定

json

对于post的提交方式,提交数据格式为application/json,需要定义一个结构体来解析请求体request的body数据,还可以使用Tag来指定json数据的key值,binding字段可以设为必选;

// 定义接收数据的结构体
type Login struct {
   // binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
   User    string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
   Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}

func main() {
   // 1.创建路由
   // 默认使用了2个中间件Logger(), Recovery()
   r := gin.Default()
   // JSON绑定
   r.POST("loginJSON", func(c *gin.Context) {
      // 声明接收的变量
      var json Login
      // 将request的body中的数据,自动按照json格式解析到结构体
      if err := c.ShouldBindJSON(&json); err != nil {
         // 返回错误信息
         // gin.H封装了生成json数据的工具
         c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
         return
      }
      // 判断用户名密码是否正确
      if json.User != "root" || json.Pssword != "admin" {
         c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
         return
      }
      c.JSON(http.StatusOK, gin.H{"status": "200"})
   })
   r.Run(":8000")
}

使用

c.ShouldBindJSON(&json)

将数据储存到了定义的结构体变量json中,再对其进行处理;

最终结果为:

image-20240622092423037

form

使用Bind()来解析form类型的数据;

// 定义接收数据的结构体
type Login struct {
    // binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
    User    string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
    Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}

func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // JSON绑定
    r.POST("/loginForm", func(c *gin.Context) {
        // 声明接收的变量
        var form Login
        // Bind()默认解析并绑定form格式
        // 根据请求头中content-type自动推断
        if err := c.Bind(&form); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }
        // 判断用户名密码是否正确
        if form.User != "root" || form.Pssword != "admin" {
            c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
            return
        }
        c.JSON(http.StatusOK, gin.H{"status": "200"})
    })
    r.Run(":8000")
}
url

对于URL中的param数据,也可以通过

c.ShouldBindUri()

来进行解析获取;

func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // JSON绑定
    r.GET("/:user/:password", func(c *gin.Context) {
        // 声明接收的变量
        var login Login
        // Bind()默认解析并绑定form格式
        // 根据请求头中content-type自动推断
        if err := c.ShouldBindUri(&login); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }
        // 判断用户名密码是否正确
        if login.User != "root" || login.Pssword != "admin" {
            c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
            return
        }
        c.JSON(http.StatusOK, gin.H{"status": "200"})
    })
    r.Run(":8000")
}

gin函数

gin.DisableConsoleColor()

禁用控制台颜色

gin.H类型

gin.H封装了生成json数据的工具,定义的map[String]any类型

gin.Default() 函数

默认赋值给变量r


r.GET(relativePath string, handlers ...gin.HandlerFunc)

作用:对于GET的restful请求方式进行捕获;

参数:第一个参数为地址路由名,第二个参数为内嵌函数用于处理逻辑;

举例:r.GET("/example", func(c *gin.Context) {})

r.POST(relativePath string, handlers ...gin.HandlerFunc)

作用:对于POST的restful请求方式进行捕获;

参数:第一个参数为相对地址路由名,第二个参数为内嵌函数用于处理逻辑;

举例:r.POST("/example", func(c *gin.Context) {})

r.LoadHTMLFiles(files ...string)

作用:gin框架获取html文件地址,可以在接口设计中使用该html文件地址;

参数:html文件地址;

举例:r.LoadHTMLFiles("index.html")

r.LoadHTMLGlob(pattern string)

作用:gin框架获取一个地址匹配项的所有html文件;

参数:正则匹配项;

举例:r.LoadHTMLGlob(".template/*")

r.Run(addr ...string)

作用:将路由器附加到http上,并开始监听服务器,不填参数默认为8080端口;

参数:port端口;

举例:r.Run(":8000")

gin.Context结构体

默认参数名为c


c.Param(key string) string

作用:捕获URL地址的param,相当于c.Params.ByName(key),有两种捕获方式 ':param' 与 *'param' ,对于第一种可以直接获得对应param的值,第二种获得开头携带有 '/' 的值;

参数:param名;

举例:name:=c.Param("name")

c.DefaultQuery(key string, defaultValue string) string

作用:捕获URL地址的query,若捕获不到数据可以使用参数defaultValue的值;

参数:第一个参数为query的参数名,第二个参数为默认query参数值;

举例:name:=c.DefaultQuery("name","lxh")

c.PostForm(key string) (value string)

作用:捕获前端表单中对应key的值;

参数:表单数据里属性name的键值;

举例:userName:=c.PostForm("userName")

c.DefaultPostForm(key string, defaultValue string) string

作用:捕获前端表单中对应key的值,若捕获不到数据可以使用参数defaultValue的值;

参数:第一个参数为属性name的参数名,第二个参数为默认name的参数值;

举例:types:=c.DefaultPostForm("type","post")

c.FormFile(name string) (*multipart.FileHeader, error)

作用:捕获前端提交的文件;

参数:前端input标签属性type为file的数据;

举例:file, err := c.FormFile("file")

c.Request.FormFile(key string) (multipart.File, *multipart.FileHeader, error)

作用:获得原生Request的formFile,可以获得更多请求的数据;

参数:前端input标签属性type为file的name值;

举例:_, headers, err := c.Request.FormFile("file")

c.SaveUploadedFile(file *multipart.FileHeader, dst string) error

作用:保存文件数据到本地中;

参数:第一个参数为二进制文件,第二个参数为保存的路径文件名;

举例:c.SaveUploadedFile(file, file.Filename)

c.String(code int, format string, values ...any)

作用:在页面中显示字符串;

参数:第一个参数为code状态码,一般可以使用net/http包的状态码常量,第二个参数为要输出的字符串;

举例:c.String(http.StatusOK,"ok")

c.HTML(code int, name string, obj any)

作用:响应返回给前端一个在服务端地址为name的html文件;

参数:第一个参数为code状态码,第二个参数为html文件地址,第三个参数为json数据;

举例:c.HTML(http.StatusOK, "index.html", gin.H{"title": "Posts",})

c.JSON(code int, obj any)

作用:在响应体中放入json数据;

参数:第一个参数为code状态码,一般可以使用net/http包的状态码常量,第二个参数为要输出的json数据,一般使用gin.H{"key" : value,...}的gin自定义类型来存储json数据;

举例:c.JSON(200, gin.H{"message": file.Filename})

c.AsciiJSON(code int, obj any)

作用:在响应体中放入只包含可以转换成ASCLL码的json数据,相比较c.JSON(),可以满足部分只准许ascll码的中间件或者客户端;

参数:第一个参数为code状态码,一般可以使用net/http包的状态码常量,第二个参数为要输出的json数据,一般使用gin.H{"key" : value,...}的gin自定义类型来存储json数据;

举例:c.JSON(200, gin.H{"message": file.Filename})

c.JSONP(code int, obj any)

作用:跨域在响应体中放入json数据,传输格式为application/javascript

参数:第一个参数为code状态码,一般可以使用net/http包的状态码常量,第二个参数为要输出的json数据,一般使用gin.H{"key" : value,...}的gin自定义类型来存储json数据;

举例:c.JSONP(200, gin.H{"message": file.Filename})

c.PureJSON(code int, obj any)

作用:在响应体中放入json数据,对特殊字符不使用unicode实体来替换,而是使用字面;

参数:第一个参数为code状态码,一般可以使用net/http包的状态码常量,第二个参数为要输出的json数据,一般使用gin.H{"key" : value,...}的gin自定义类型来存储json数据;

举例:c.JSONP(200, gin.H{"message": <br>加粗</br>})

c.SecureJSON(code int, obj any)

作用:在响应体中放入json数据,并为了防止劫持json的列表数据,默认增加 while(1); 前缀,也可以通过r.SecureJsonPrefix(")]}',\n")自定义前缀,传输格式为application/json

参数:第一个参数为code状态码,一般可以使用net/http包的状态码常量,第二个参数为要输出的json数据,一般使用gin.H{"key" : value,...}的gin自定义类型来存储json数据;

举例:c.SecureJSON(http.StatusOK,[]string{"hello"," world"})

c.MultipartForm() (*multipart.Form, error)

作用:获得解析后的多表单数据集合;

c.ShouldBindJSON(obj any) error

作用:将request的body中的数据,自动按照json格式解析到结构体;

参数:属于json类型的指针变量;

举例:err := c.ShouldBindJSON(&json)

c.ShouldBindUri(obj any) error

作用:将url中的param,解析到结构体中;

参数:属于json类型的指针变量;

举例:err := c.ShouldBindUri(&login)

c.ShouldBind(obj any) error

作用:自动解析数据格式,对于解析失败,可以在服务端捕捉报错信息,并对返回的数据进行处理;

c.Bind(obj any) error

作用:根据Method与Content-Type来自动选择解析的类型,相对于ShouldBind()Bind()会在解析失败后自动返回400状态码的报错;

参数:指针变量;

举例:err := c.Bind(&form);

c.ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error)

作用:按照参数bb的类型来解析body数据,并可以多次复用;

参数:第一个参数为指针变量,第二个参数是对应解析的类型;

举例:errB := c.ShouldBindBodyWith(&obj,binding.JSON);

c.DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string)

作用:用于从 io.Reader 接口中读取数据并发送给客户端的高级响应方法,适用于处理动态生成或大数据量的响应场景;

参数:第一个参数为code状态码,第二个参数为二进制文本长度,第三个参数为传输格式,第四个参数为response响应的body,第五个参数为附加json数据;

举例:c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)

c.Cookie(name string) (string, error)

作用:获得客户端cookie中key为name的value值;

参数:cookie的key值;

举例:c.Cookie("gin_cookie")

c.SetCookie(name string, value string, maxAge int, path string, domain string, secure bool, httpOnly bool)

作用:在客户端中创建一个cookie数据,key为name,并设置了过期时间maxAge,cookie可访问的路径path(若为“/”则默认为全网站都可以访问),cookie的域名(若为"localhost"就是本地可访问),secure为true则只允许HTTPS协议访问,为false则可以允许HTTP协议访问,httpOnly为true则不会被客户端的JavaScript代码访问;

参数:第一个参数为cookie的key值,第二个参数为value值,第三个参数为超时时间(s),第四个参数为可访问路径,第五个参数为可访问ip,第六个参数为是否允许安全协议,第七个参数为是否允许客户端JavaScript代码访问;

举例:c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)