go进阶(2) gin框架

go进阶(2) gin框架


gin框架使用

请求处理

一般常用的请求类型有GET、POST,俩者的数据处理方式并不相通,针对 go进阶(1) gin框架-CSDN博客 的数据解析与绑定一节进行新增与汇总;

GET请求

对于GET请求,服务端只需要对包含在URL中的query进行处理,使用

c.Query()

即可以获取query数据;

POST请求

对于POST请求,http常见的传输格式为四种:

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

第一种可以采用
c.ShouldBindJSON()

解析请求体中的json数据;

第二种可以采用

c.PostForm()

获得单个key的值,也可以通过

c.ShouldBind()

来自动解析请求体中的数据;

第三种采用

c.ShouldBindXML()

解析请求体中的xml数据;

对于前三种,都可以使用

c.ShouldBind()

来自动解析对应类型的数据,相当于一个分析器,对于解析错误,可以对报错数据进行捕捉并返回自定义的相应数据;

c.Bind()

也可以自动解析对应类型的数据,但如果解析错误,则自动返回400状态码的报错;

第四种传输格式一般是文件类型的数据所使用的,一般采用

c.FormFile() 或是 c.Request.FormFile()

来对文件类型数据进行处理。

若在服务端需要多次绑定结构体,则需要使用

c.ShouldBindBodyWith()

可以读取c.Request.body并将结果存储到上下文,可以服用存储在上下文的body;

func SomeHandler(c *gin.Context) {
  objA := formA{}
  objB := formB{}
  // 读取 c.Request.Body 并将结果存入上下文。
  if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
    c.String(http.StatusOK, `the body should be formA`)
  // 这时, 复用存储在上下文中的 body。
  } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
    c.String(http.StatusOK, `the body should be formB JSON`)
  // 可以接受其他格式
  } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
    c.String(http.StatusOK, `the body should be formB XML`)
  } else {
    ...
  }
}

HTML渲染

gin框架支持返回text/html格式的数据,导入包"html/template"

func main(){
	r := gin.Default()
	r.LoadHTMLGlob("templates/*")
	//r.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
	r.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"title": "Main website",
		})
	})
	r.Run(":8080")
}
}

使用

r.LoadHTMLGlob("templates/*")

加载templates目录下所有的html文件,相当于r.LoadHTMLFiles("templates/template1.html", "templates/template2.html")

逐个加载文件,在接口设计中添加

c.HTML()

使得响应返回text/htm格式的文件;

JSON响应

在Gin框架中,这四个方法是用于生成不同类型JSON响应的函数。让我们逐个介绍它们的作用和区别:

c.PureJSON()

​ 用于发送原始的JSON响应,不会进行Unicode或HTML字符的转义,适合在已经确保数据安全性的情况下使用,例如在内部系统间的通信或对JSON格式有严格要求的场景。

举例:

c.PureJSON(http.StatusOK, []byte(`{"message": "<b>Hello, JSON!</b>"}`))

这将直接返回

{"message": "<b>Hello, JSON!</b>"}

,而不会将<b>转义为\u003cbr\u003e;

c.JSON()

用于将Go结构体或map[string]interface{}转换为JSON格式,并将其作为HTTP响应返回。

默认情况下,它使用UTF-8编码并对非ASCII字符进行转义处理。

例如:

c.JSON(http.StatusOK, gin.H{"message": "Hello, JSON!"})

这将返回一个JSON响应

{"message": "Hello, JSON!"}

c.AsciiJSON()

c.AsciiJSON() 方法与 c.JSON() 类似,但是会将所有非ASCII字符转换为转义序列,确保响应只包含ASCII字符,适用于某些客户端或中间件只能处理ASCII字符的情况;

举例:

c.AsciiJSON(http.StatusOK, gin.H{"message": "Hello, ASCII JSON!"})

这将返回

{"message": "Hello, ASCII JSON!"}

的JSON响应,其中非ASCII字符会被转义;

c.JSONP()

用于支持JSONP(JSON with Padding),允许跨域请求从服务器获取数据;

举例:

c.JSONP(http.StatusOK, gin.H{"message": "Hello, JSONP!"})

这将返回一个类似于

{"message": "Hello, JSONP!"} 

c.SecureJSON()

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

举例:

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

这将返回

while(1);["hello"," world"]

选择使用哪种方法 取决于你的应用场景和需求:

  • 如果需要原始的、不经过转义处理的JSON响应,可以使用 c.PureJSON();
  • 如果需要普通的JSON响应,并且能够处理Unicode字符,可以使用 c.JSON();
  • 如果需要确保所有响应只包含ASCII字符,可以使用 c.AsciiJSON();
  • 如果需要支持跨域请求,可以使用 c.JSONP()
  • 如果需要保护json列表数据,则使用c.SecureJSON();

安全页眉

使用安全标头保护网络应用程序免受常见安全漏洞的攻击非常重要,本示例展示如何在 Gin 应用程序中添加安全标头,以及如何避免与主机标头注入相关的攻击(SSRF、开放重定向);

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

	expectedHost := "localhost:8000"

	// Setup Security Headers
	r.Use(func(c *gin.Context) {
		if c.Request.Host != expectedHost {
			c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid host header"})
			return
		}
		c.Header("X-Frame-Options", "DENY")
		c.Header("Content-Security-Policy", "default-src 'self'; connect-src *; font-src *; script-src-elem * 'unsafe-inline'; img-src * data:; style-src * 'unsafe-inline';")
		c.Header("X-XSS-Protection", "1; mode=block")
		c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
		c.Header("Referrer-Policy", "strict-origin")
		c.Header("X-Content-Type-Options", "nosniff")
		c.Header("Permissions-Policy", "geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()")
		c.Next()
	})

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

	r.Run(":8000")
}

程序启动后,使用浏览器打开http://localhost:8000/ping地址,F12打开检查工具,在网络功能中打开名为ping的请求,在表头可以查看服务端设置的安全保护响应标头,如下图:

在这里插入图片描述

render读取数据

​ 在项目上线后,往往需要将产生的日志保存在固定的log文件中,方便进行记录与查看。我们需要项目日志输出的默认位置进行修改才能够实现以上功能;

代码如下:

func main() {
	router := gin.Default()
	router.GET("/someDataFromReader", func(c *gin.Context) {
		response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png")
		if err != nil || response.StatusCode != http.StatusOK {
			c.Status(http.StatusServiceUnavailable)
			return
		}

		reader := response.Body
		contentLength := response.ContentLength
		contentType := response.Header.Get("Content-Type")

		extraHeaders := map[string]string{
			"Content-Disposition": `attachment; filename="gopher.png"`,
		}

		c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
	})
	router.Run(":8080")
}

此代码先通过http.Get()远程请求网络数据,获得reponse,再获得reponse响应里的body,contentLength,content-Type,还创建一个附加的json格式请求头,将其放入c.DataFromReader()中,将此render数据与附加数据发送到客户端;

日志记录

func main() {
    // 禁用控制台颜色,将日志写入文件时不需要控制台颜色。
    gin.DisableConsoleColor()

    // 记录到文件。
    f, _ := os.Create("gin.log")
    gin.DefaultWriter = io.MultiWriter(f)

    // 如果需要同时将日志写入文件和控制台,请使用以下代码。
    // gin.DefaultWriter = io.MultiWriter(f, os.Stdout)

    router := gin.Default()
    router.GET("/ping", func(c *gin.Context) {
        c.String(200, "pong")
    })

    router.Run(":8000")
}

使用os包在当前目录下创建log文件,获得f文件指针,将gin.DefaultWriter的gin框架默认日志io流改为io包获得f文件的io流即可;

设置与获取Cookie

​ 对于客户端用户登录的信息凭证可以存储在前端的cookie中,在用户使用api时,可以在客户端设置一个拦截器,对用户信息凭证进行检测,在服务端中将储存在cookie中的数据进行获取与分析,若用户凭证正确则允许使用api;

func main() {
    router := gin.Default()
    router.GET("/cookie", func(c *gin.Context) {
        cookie, err := c.Cookie("gin_cookie")
        if err != nil {
            cookie = "NotSet"
            c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
        }
        fmt.Printf("Cookie value: %s \n", cookie)
    })
    router.Run()
}

​ 该代码使用c.Cookie()来获得对应key的value值,并使用c.SetCookie()设置了一个cookie值给客户端,具体函数功能请看后面函数描述一节;

gin函数

gin.DisableConsoleColor()

禁用控制台颜色

gin.H类型

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

gin.New() 函数

没有任何默认中间件的路由;

gin.Default() 函数

gin默认使用的路由组,一般赋值给变量r;

r.Use(middleware ...gin.HandlerFunc) gin.IRoutes

作用:将中间件添加到组中;

参数:封装好的中间件;

举例:r.Use(gin.Recovery())

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)

  • 46
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值