小工具      在线工具  汉语词典  dos游戏  css  js  c++  java

Gin web框架初步认识

# Gin,gin,golang 额外说明

收录于:17天前

Goland使用及gin框架下载引入

第一次使用Goland时,需要配置GOROOT、GOPATH和Go Modules。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

配置完成后进入面板,右键选择Go Modules文件,或者在go工作区通过命令go mod init [name]创建go mod项目。

在这里插入图片描述
创建完的项目一般都有go.mod文件和go.sum,前者是go包的依赖管理文件,后者是体现依赖管理的关系。
在这里插入图片描述

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

go get命令更新主模块的 go.mod 文件中的模块依赖关系,模块的go.mod文件记录了它需要哪些版本的依赖项。这些依赖项的源代码存储在本地缓存中。

go get更新go.mod文件中列出的要求。它还确保这些需求是自洽的,并根据需要添加新需求,以便您在命令行中命名的包导入的每个包都由您的需求中的某个模块提供。
作为更新和添加需求的副作用,go get还将包含命名包(及其依赖项)的模块下载到本地模块缓存。
go mod download 不添加新需求或更新现有需求。它只下载您请求的特定模块版本,也就是说模块存在于本地缓存,不需要go get再次远程下载,在go.mod引入后go mod download从缓存中加载。

gin框架下载后会在GOPATH中的pkg/mod下。

在这里插入图片描述

创建一个包并编写Web引擎:

package main

import "github.com/gin-gonic/gin"

func main() {
    
	//创建一个服务器引擎
	engine := gin.Default()
	//配置web容器地址
	engine.GET("/", func(context *gin.Context) {
    
		context.JSON(200, gin.H{
    "msg": "Hello World"})
	})
	//配置服务器端口
	engine.Run(":8080")
}

在这里插入图片描述

Gin路由

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {
    
    r := gin.Default()
    r.GET("/", func(c *gin.Context) {
    
        c.String(http.StatusOK, "hello word")
    })
    r.POST("/xxxpost",HandlerFunc)
    //监听端口默认为8080
    r.Run(":8000")
}

在上面函数中,创建了一个gin引擎,利用引擎设置路由规则,不同地址对应不同的方法,这个方法只能是HandlerFunc类型的方法即参数为gin.context
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes

type HandlerFunc func(*Context)

因此,该方法可以直接使用匿名方法。最后通过引擎的Run方法绑定端口。

Restful风格

gin引擎支持Restfu风格。 gin框架封装了http库,提供了GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS等http请求方法。

router := gin.Default()
router.GET("/get", func(c *gin.Context) {
     c.JSON(200, gin.H{
    "message": "get方法"}) })
router.POST("/post", func(c *gin.Context) {
     c.JSON(200, gin.H{
    "message": "post方法"}) })
router.PUT("/put", func(c *gin.Context) {
     c.JSON(200, gin.H{
    "message": "put方法"}) })
router.DELETE("/delete", func(c *gin.Context) {
     c.JSON(200, gin.H{
    "message": "delete"}) })
router.PATCH("/patch", func(c *gin.Context) {
     c.JSON(200, gin.H{
    "message": "patch"}) })
router.HEAD("/head", func(c *gin.Context) {
     c.JSON(200, gin.H{
    "message": "head"}) })
router.OPTIONS("/options", func(c *gin.Context) {
     c.JSON(200, gin.H{
    "message": "options"}) })
router.Run(":80")//指定端口 localhost:9999
engine.PATCH("/url", func(context *gin.Context) {
    
	
})
重定向

context.Redirect(code,rrl)方法实现重定向。

engine.GET("/redirect", func(context *gin.Context) {
    
	context.Redirect(301, "https://baidu.com")
})
错误页面

出现404页面的错误是没有找到对应的路由,一般情况服务器都有默认的404页面,但是有时需要定制的页面。就需要使用engine.NoRoute(func(context *gin.Context)方法。

/** gin默认又模板引擎 */

//加载静态页面
engine.LoadHTMLGlob("./templates/*")

//404页面没有路由
engine.NoRoute(func(context *gin.Context) {
    
	context.HTML(404, "404.html", nil)
})
路由组

除了使用匿名函数之外,还可以直接定义处理方法。定义的方法一般是在路由组中,如下

v1 := r.Group("/v1")
{
    
	v1.GET("/login", login)
	v1.GET("/register", register)
}

v2 := r.Group("/v2")
{
    
	v2.GET("/index", page)
	v2.GET("/main", index)
}
r.Run()

func login(c *gin.Context) {
    
	c.String(200, "login method")
}

func register(c *gin.Context) {
    
	c.String(200, "register method")
}

func page(c *gin.Context) {
    
	c.String(200, "page main")
}

func index(c *gin.Context) {
    
	c.String(200, "index main")
}

每个r.Group("/v2")管理若干路由地址。

返回数据或页面

import (
	"github.com/gin-gonic/gin"
)

func main() {
    
	//创建一个服务器引擎
	engine := gin.Default()

	//加载静态页面
	engine.LoadHTMLGlob("./templates/*")
	//配置web容器地址
	engine.GET("/", func(context *gin.Context) {
    
		//返回json数据
		context.JSON(200, gin.H{
    "msg": "Hello World"})
	})
	engine.GET("/index", func(context *gin.Context) {
    
		//返回html页面
		context.HTML(200, "index.html", gin.H{
    
			"msg": "gin后台转递的数据",
		})
	})
	//配置服务器端口
	engine.Run(":8080")
}

gin.Context参数的JSON方法用于返回json数据,HTML方法返回页面,且是模板引擎的页面,需要使用引擎加载html文件engine.LoadHTMLGlob("./templates/*")

html文件中通过{ {}}获取传递的数据,.数据接收。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Hello World</h1>

{
   {.msg}}
</body>
</html>

在这里插入图片描述

参数处理

在web容器中,第一步有web容器处理统一资源定位符,分发到不同的路由地址,到达地址的数据均有两个参数接收与处理。在Gin中gin.Context参数,封装了request(请求参数)和response(响应参数),因此基于参数的处理都需要使用gin.Context参数。(Java是没有封装的)参数处理又分为请求行参数、请求头和请求体参数。

url?name=xxx&password=xxx
//url?name=xxx&password=xxx
engine.GET("/add", func(context *gin.Context) {
    
	name := context.Query("name")
	pass := context.Query("pass")
	context.JSON(200, gin.H{
    
		"user": name,
		"pass": pass,
	})
})

通过context.Query()查询参数的值。也可以使用DefaultQuery(),区别在于Query()没对应,返回空字符串,DefaultQuery()返回默认值,其有两个参数,第一个为绑定的查询变量,第二个为默认值。

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

在这里插入图片描述

url/value1/value2
//url/value1/value2
engine.GET("/delete/:name/:pass", func(context *gin.Context) {
    
	name := context.Param("name")
	pass := context.Param("pass")
	context.JSON(200, gin.H{
    
		"name": name,
		"pass": pass,
	})
})

通过context.Param()获取获取参数的值。这是Restful风格的传参。url地址上的标识符需要前面加上:/delete/:name/:pass
在这里插入图片描述

接收json数据
//接收json数据
engine.POST("/json", func(context *gin.Context) {
    
	data, err := context.GetRawData()
	if err != nil {
    
		panic(err)
	}
	context.JSON(200, data)
})

context.GetRawData()用于接收请求体数据,(这个很好记)

在这里插入图片描述
在这里插入图片描述

在主流的API测试工具中JSON数据均被定义为raw数据。

context.GetRawData()返回的是字节数组,需要字节数组转化为Go的内置对象如结构体。

在这里插入图片描述

在Go序列化章节中说到,序列化结构体的返回值为字节数组,转为json字符串需要使用string()方法Go语言流处理、工厂模式、命令参数、序列化、单元测试。因此将其转化为字符串类型。

context.JSON(200, string(data))

在这里插入图片描述

但这种转换没有任何意义。 Go 不能使用该字符串单词。实际中,一般都是通过反序列化的方式将字节数组直接转换为Go语言的内置对象。

type Person struct{
    
	Name string
	Age int
	Phone int8
	Address string
}
//接收json数据
engine.POST("/json", func(context *gin.Context) {
    
	data, _ := context.GetRawData()
	//context.JSON(200, string(data))
	var per Person
	_ = json.Unmarshal(data, &per)
	context.JSON(200, per)
})

在这里插入图片描述

定义了一个结构来接收数据,但是出现了一个新问题。反序列化时,字符串名称必须与结构体成员名称一致,因此存在需要序列化的标签规则。修改后的结构如下:

type Person struct {
    
	Name    string `json:"name"`
	Age     int    `json:"age"`
	Phone   string `json:"phone"`
	Address string `json:"address"`
}

改造后只要传入的数据和json:[name]的name对应即可。

表单数据

表单数据通过context.PostForm方法接收,其参数对应html的input标签的name属性即可。

//表单数据
engine.POST("/form", func(context *gin.Context) {
    
	user := context.PostForm("user")
	pass := context.PostForm("pass")

	context.JSON(200, gin.H{
    
		"username": user,
		"password": pass,
	})
})

在这里插入图片描述

表单传输为post请求,http常见的传输格式为四种:
application/json
application/x-www-form-urlencoded
application/xml
multipart/form-data

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

文件上传

<!DOCTYPE html>
<html>
    <head>
        <title>test</title>
        <meta lang="en" charset="utf-8">

    </head>  
    <body>

        <form action="/upload" method="post" enctype="multipart/form-data">
            upload file<input type="file" name="file">
            <input type="submit" value="submit">
        </form>

    </body>
</html>
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

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

	r.LoadHTMLGlob("./templates/*")

	r.GET("/", func(ctx *gin.Context) {
    
		ctx.HTML(200, "index.html", nil)
	})

	r.POST("/upload", func(c *gin.Context) {
    
		file, err := c.FormFile("file")
		if err != nil {
    
			c.String(500, "上传图片出错")
		}
		c.SaveUploadedFile(file, file.Filename)
		c.String(http.StatusOK, file.Filename)
	})
	r.Run()

}

文件将被上传到根目录,如下:

在这里插入图片描述

中间件(拦截器)

中间件是在路由请求前的预处理,相当远Java的拦截器。中间件也是封装在gin.context中,定义在单个路由就对某个接口生效,定义在全局就对整体生效。

//中间件(拦截器)
func preHandler() gin.HandlerFunc {
    
	return func(context *gin.Context) {
    
		//设置值
		context.Set("isLog",true)
		//放行
		context.Next()
		//阻止
		context.Abort()
	}
}

加载静态资源

//加载静态页面
engine.LoadHTMLGlob("./templates/*")
//加载静态资源
engine.Static("/static", "./static")

前后端交互结构体绑定数据

json

import (
	"encoding/json"

	"github.com/gin-gonic/gin"
)

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

	engine.GET("/", func(ctx *gin.Context) {
    
		ctx.String(200, "Hello World")
	})

	engine.GET("/index", func(ctx *gin.Context) {
    
		data, _ := ctx.GetRawData()
		var log Login
		_ = json.Unmarshal(data, &log)
		ctx.JSON(200, log)
	})
	engine.Run("127.0.0.1:8080")
}

type Login struct {
    
	User string `json:"username"`
	Pass string `json:"password"`
}

除了上述通过ctx.GetRawData()接收字节数组外,也可以直接gin封装的结构体函数将其绑定到结构体数据上。ctx.ShouldBindJSON()方法将传递的json数据直接绑定到结构体上,而不需要二次转化,要求是字段对应,或者序列化的tag规则即可。如下:

func main() {
    
	engine := gin.Default()
	engine.GET("/index", func(ctx *gin.Context) {
    
		var log Login
		_ = ctx.ShouldBindJSON(&log)
		ctx.JSON(200, log)
	})
	engine.Run("127.0.0.1:8080")
}

type Login struct {
    
	User string `json:"username"`
	Pass string `json:"password"`
}

表单数据

注意这里的form-data不是x-www-form-urlencoded。form-data的数据通过序列化tag规则的form标签,如下:

在这里插入图片描述

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

	engine.GET("/per", func(ctx *gin.Context) {
    
		var per Person
		ctx.Bind(&per)
		ctx.JSON(200, per)
	})

	engine.Run("127.0.0.1:8080")

}

type Person struct {
    
	Name string `form:"username"`
	Age  int    `form:"age"`
}

在这里插入图片描述
x 形式 urlencoded

x-form-urlencoded的转化使用的是ctx.ShouldBind()方法。如下:

type LoginForm struct {
    
	User     string `form:"user" binding:"required"`
	Password string `form:"password" binding:"required"`
}

func main() {
    
	router := gin.Default()
	router.POST("/login", func(c *gin.Context) {
    
		// 你可以使用显式绑定声明绑定 multipart form:
		// c.ShouldBindWith(&form, binding.Form)
		// 或者简单地使用 ShouldBind 方法自动绑定:
		var form LoginForm
		// 在这种情况下,将自动选择合适的绑定
		_ = c.ShouldBind(&form)
		c.JSON(200, form)
	})
	router.Run("127.0.0.1:8080")
}

url数据绑定结构

url绑定结构体只支持url?user=xxx&pass=xxx的风格

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

	engine.GET("/per/:username/:age", func(ctx *gin.Context) {
    
		var per Person
		ctx.ShouldBindUri(&per)
		ctx.JSON(200, per)
	})

	engine.Run("127.0.0.1:8080")

}

type Person struct {
    
	Name string `uri:"username"`
	Age  int    `uri:"age"`
}

结构体序列化tag标签改为uri,方法使用ctx.ShouldBindUri()

将数据直接绑定结构体,会更加快捷,但是需要注意,使用序列化的tag规则定义结构体,另外还有一个binding:"required“标签,如果存在,则绑定后赋值不能为空否则回报错。
多个标签也可以一起用form:"username" json:"user" uri:"user" xml:"user" binding:"required"

前后端交互响应数据

上一节讲解了前端传递过来的数据可以直接转换为结构数据,也可以通过序列化转换为其他数据类型。在Gin框架中,对这些过程进行了封装,可以通过相关方法直接将数据转换为相应的数据类型。

//JSON
engine.GET("/per", func(ctx *gin.Context) {
    
	var per Person
	ctx.ShouldBindUri(&per)
	ctx.JSON(200, per)
})

ctx.JSON()方法可以直接将Go的一些内置对象直接转化为json字符串。最常见的就是字典,结构体等。

// XML
r.GET("/someXML", func(c *gin.Context) {
    
    c.XML(200, gin.H{
    "message": "abc"})
})

ctx.XML()方法可以直接将数据转为XML文件的格式。

Gin模板引擎

如果用gin返回html页面,就会默认使用模板引擎,gin使用LoadHTMLGlob()方法加载模板文件。

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

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

r.LoadHTMLGlob("tem/*")方法加载文件下所有模板文件,通过gin.Context.HTML()方法渲染模板引擎,方法又三个参数,第一个状态码,第二个是模板文件,第三个是数据。

在模板引擎中通过.来获取数据。一般情况下通过gin.H返回的是一个map类型,通过.key获取。

gin中html文件也能模块化,通过{ { define ...}}定义模块{ { template ...}}引入模块。

在这里插入图片描述

<!--header.html-->
{
   {define "header"}}
<h1>this is header</h1>

{
   {end}}
<!--footer.html-->
{
   { define "footer"}}
<h1>this is footer</h1>

{
   {end}}
<!--index.html->

<!DOCTYPE html>
<html>
    <head>
        <title>hello</title>
        <meta>
    </head>
    <body>
        {
   {define "index.html"}}
        <!--header-->
        {
   { template "header" .}}


        <!--main-->
        <h1>this is main</h1>

        <!--footer-->
        {
   { template "footer" .}}

        {
   { end }}
    </body>
</html>
{
   {define "header"}}
...
{
   { end }}

通过上面的关键字定义html模块,header为一个路径,分别是根路径递归,最后一个是模块名,如果header.html在head目录下,那么其路径对应的是head/header。另外还受engine.LoadHTMLGlob("templates/*")方法的影响,一般该方法加载到资源的更目录或者html文件的父目录即可。在结合define关键字定义路径。

在其他模板引入时注意不要忘了.{ { template "footer" .}},主模块也需要定义一下,将被引入模块包裹起来即{ {define "index.html"}},这里直接定义为文件的名称即可。

package main

import "github.com/gin-gonic/gin"

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

	engine.LoadHTMLGlob("templates/*")

	engine.GET("/", func(ctx *gin.Context) {
    
		ctx.HTML(200, "index.html", nil)
	})

	engine.Run("127.0.0.1:8080")

}

在这里插入图片描述

日志

import (
	"io"
	"os"

	"github.com/gin-gonic/gin"
)

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("127.0.0.1:8080")
}

gin.DisableConsoleColor()禁用控制台颜色控制台没有日志
os.Create("gin.log")创建日志文件
gin.DefaultWriter = io.MultiWriter(f)写入日志文件
开启日志后日志会记录到日志文件中去。

gin在路由时会打印日志,这个日志的格式也是可以自定义的,请移步定义路由日志的格式

BasicAuth 中间件

Basic Auth是一种开放平台认证方式,简单的说就是需要你输入用户名和密码才能继续访问。BasicAuth授权认证中间件的使用

官方网站:杜松子酒网络框架

. . .

相关推荐

额外说明

Java内存泄漏和内存溢出详解(InsCode AI创作助手)

在Java开发中,内存管理是一个非常重要的话题。其中,内存泄露和内存溢出是两个常见的问题。本文将对这两个问题进行详细的解析和比较,并提供相应的示例及解决方案。 一、Java内存泄露 内存泄露是指程序申请了一块内存空间,使用完后没有释放掉,导致系统无法再次

额外说明

企业内部EHR系统渗透测试3245324

1、渗透前言 对EHR系统访问时,首先是需要一个登录的,作为企业员工,大部分系统接入了SSO,该EHR系统为了保证安全性也不例外。因此对该系统的渗透测试基础的前提是————在该系统拥有一个可登录的凭据,但是该凭据并未拥有任何权限。 如下所示,正常的企业员

额外说明

在vue中创建一个新项目

vue新建一个项目 使用前提: 效果如图:

额外说明

leetcode84(直方图中最大的矩形:单调堆栈)

给定 n 个非负整数,构成数组int[ ] heights用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2

额外说明

SQL查询优化---子查询优化、排序分组优化、覆盖索引优化

1、子查询优化 1、尽量不要使用not in 或者 not exists 取所有不为掌门人的员工,按年龄分组 ,每个年龄段多少人 SELECT SQL_NO_CACHE age,count(*) FROM emp a WHERE id NOT

额外说明

ChatGLM2-6B Lora 微调训练医疗问答任务

一、ChatGLM2-6B Lora 微调 LoRA 微调技术的思想很简单,在原始 PLM (Pre-trained Language Model) 增加一个旁路,一般是在 transformer 层,做一个降维再升维的操作,模型的输入输出维度不变,来模

额外说明

4.讲究先来后到的队列

概述 目标: 队列的存储结构及操作特点 java中队列相关的api 基于单链表的队列实现 刷题(设计循环队列) 存储结构及特点 队列(queue) 和栈一样,代表具有一类操作特征的数据结构,拿日常生活中的一个场景举例说明,去车站的窗口买票,就要排队,先来

额外说明

Python基础 —— 运算符

Python学习系列文章:- 目录 - 文章目录 〇、概述 一、算数运算符 1. 算数运算符 2. 示例:两数之间的算数运算操作 二、赋值运算符 1. 赋值运算符 2. 示例:计算 10 与 20 的和 三、比较运算符 1. 比较运算符 2. 示例:两数

额外说明

MySQL 存储过程

MySQL 存储过程 存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中

额外说明

Socket套接字

什么是套接字 网络协议栈通过套接字向引用程序提供传输数据的服务。 套接字是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。通过应用程序对socket进行操作完成数据通讯,而不用关注数据具体是如何传输到另外的一个网络进程。 套接字作用 通过套接字

ads via 小工具