• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

GinGin框架路由

武飞扬头像
童话ing
帮助1

前言

从Java转Go,初学Gin框架,最关心的操作就是接收请求与返回响应的问题,本文主要简单介绍一下Gin框架的基本使用。Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点,对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错。借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范。

Go中下载Gin比较简单,通过go get即可拉取到本地(前提是本地的工作区、代理等环境已经配置好)

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

一、路由

前端请求到达后端如何被处理,这就需要进行路由去匹配相应的handler方法执行了。

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

二、Restful风格的API

gin支持Restful风格的API。即Representational State Transfer的缩写。直接翻译的意思是"表现层状态转化",是一种互联网应用程序的API设计理念:URL定位资源,用HTTP描述操作。

获取: /blog/getXxx -------------->Get blog/Xxx
新增: /blog/addXxx-------------->POST blog/Xxx
修改: /blog/updateXxx-------------->PUT blog/Xxx
删除: /blog/delXxxx -------------->DELETE blog/Xxx

三、参数获取

3.1 API参数

gin.Context中封装了request和response,API中的参数可以通过Context的Param方法来获取API参数。

import (
    "net/http"
    "strings"

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

func main() {
    r := gin.Default()
        //请求url为:localhost:8888/user/tonghua/233
    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)
    })
    //请求url为:localhost:8888/blog/2022/4
	r.GET("/blog/:year/:month", func(c *gin.Context) {
		year := c.Param("year")
		month := c.Param("month")
		c.JSON(http.StatusOK, gin.H{
			"year":  year,
			"month": month,
		})
	})
    //默认为监听8080端口,注意前面的冒号
    r.Run(":8888")
}

3.2 URL参数

URL参数可以通过DefaultQuery()Query()方法获取,其中DefaultQuery()可指定默认参数,如果参数不存在则返回默认值。Query()获取参数如果不存在,则会返回空串。

r.GET("/hello", func(c *gin.Context) {
	name := c.Query("name") 
	//name := c.DefaultQuery("name","tonghua")
	name, ok := c.GetQuery("name")
	if !ok {
		name = "tonghua" //取不到值,手动赋值
	}
	c.String(http.StatusOK, name)
})

3.3 表单参数

为了进行模拟这里写了两个简单的页面login.html(登录发送post请求携带表单参数)index.html(用于登录成功后跳转示例)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
  <form action="/login" method="post" novalidate autocomplete="on">
      <div>
          <label for="username">username:</label>
          <input type="username" name="username" id="username">
      </div>
      <div>
          <label for="password">password:</label>
          <input type="password" name="password" id="password">
      </div>
      <div>
          <input type="submit" value="登录">
      </div>
  </form>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>用户名:{{ .Username }}</h1>
<h1>密码是:{{ .Password }}</h1>
</body>
</html>

表单参数可通过PostForm进行获取,获取不到返回空。GetPostForm可以通过bool类型的返回值判断是否获取到参数值。当然也可使用DefaultPostForm设置默认值,获取不到则返回默认值。默认解析的是x-www-form-urlencodedfrom-data格式的参数。

r.LoadHTMLFiles("./login.html", "./index.html")  
r.GET("/login", func(c *gin.Context) {
	c.HTML(http.StatusOK, "login.html", nil)
})
r.POST("/login", func(c *gin.Context) {
	//获取form表单中的数据,PostForm获取不到数据返回nil
	username := c.PostForm("username")
	password := c.PostForm("password")
	//username1, ok := c.GetPostForm("username") //通过ok判断是否获取到值
	//if !ok {
	//	username1 = "dingli"
	//}

	//username = c.DefaultPostForm("username","defaultValue") //设置默认值
	c.HTML(http.StatusOK, "index.html", gin.H{
		"Username": username,
		"Password": password,
	})
})

HTTP数据传输的几种格式:

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

3.4 参数绑定

很多时候,我们请求的参数在后端都是一个model,其实就是一个struct而已,只是前端传递时候参数格式和go中参数格式有些不一样,比如go内部往往为了结构体参数可见,字段的首字母大写(为了反射时能成功获取),而前端中使用的是小写,因此可以借助结构体的tag标签解决。为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中URI、form表单、JSON、QueryString、XML等参数到结构体中。

3.4.1 Json参数绑定

Json数据绑定有如下三种方式:

c.BindJSON(&userInfo)
c.ShouldBindJSON(&userInfo)
这两种方式进行两次绑定时候,第二次绑定失效。ShouldBindJSON方法是最常用解析JSON数据的方法之一,但在重复调用的情况下会出现EOF的报错,这个原因出在ShouldBindJSON在调用过一次之后context.request.body.sawEOF的值是false导致,所以如果要多次绑定多个变量,需要使用ShouldBindBodyWith,如c.ShouldBindWith(&userInfo,binding.JSON)

var userInfo UserInfo
_ = c.ShouldBindJSON(&userInfo)
var userInfo1 UserInfo
_ = c.ShouldBindJSON(&userInfo1) //第二次绑定会失败,ShouldBindJSON只能绑定一次

绑定JSON的示例:({"username": "tonghua", "password": "123456"}) 可直接在postmanbody中选择raw写原始json数据进行测试

type UserInfo struct {
	Username string `form:"username" json:"username"` //设置form和json使用时名称等方便进行参数绑定  binding:"required"
	Password string `form:"password" json:"password"`
}

func main() {
	r := gin.Default()
	// 绑定表单参数,使用Bind或者ShouldBind方法进行参数绑定
	r.POST("/user", func(c *gin.Context) {
		var userInfo UserInfo //go中都是值传递,因此需要改变传递地址
		if err := c.ShouldBind(&userInfo); err == nil {
			fmt.Printf("login info: %#v\n", userInfo)
			c.JSON(http.StatusOK, gin.H{
				"username": userInfo.Username,
				"password": userInfo.Password,
			})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})
	r.Run(":8081")
}

3.4.2 表单参数绑定

利用Bind方法即可绑定表单参数,绑定form表单示例 :(username=tonghua&password=123456)

// 定义接收数据的结构体
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")
}
3.4.3 URI参数绑定

绑定URI类型参数可借助于ShouldBindUri方法完成:

// 定义接收数据的结构体
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.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")
}

还有一种是QueryString类型的参数,示例(/loginQuery?username=tonghua&password=123456),所有的绑定都可以使用ShouldBind方法完成。

四、文件上传

数据格式中,multipart/form-data格式用于文件上传。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>
   <form action="/single-upload" method="post" enctype="multipart/form-data">
       <div><input type="file" name="f1"></div>
       <div><input type ="submit" value="上传"></div>
   </form>
</body>
</html>

4.1 单文件上传

处理multipart forms提交文件时默认的内存(缓冲区)限制是32MiB,超过了则会下载到一个临时文件里,服务端则会从临时文件中读取刷盘。

r.MaxMultipartMemory = 8<<20 //8MiB ,修改缓冲区大小
r.POST("/single-upload", func(c *gin.Context) {
	file, err := c.FormFile("f1")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": err.Error(),
		})
		return
	}
	log.Println(file.Filename)
	dst := fmt.Sprintf("E:/5120154230GoCode/src/gin_parambind/%s", file.Filename) //路径需要存在
	//dst = path.Join("./file/",file.Filename) //两种方式均可得到路径
	err = c.SaveUploadedFile(file, dst) //上传文件到指定的目录
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"message": fmt.Sprintf("'%s' upload!", file.Filename),
	})
})

4.2 多文件上传

多文件上传和单文件上传区别就在于需要循环处理文件。

//多文件上传
r.POST("/multi-upload", func(c *gin.Context) {
	// Multipart form
	form, _ := c.MultipartForm()
	files := form.File["file"] //获取所有的文件
	for index, file := range files { //遍历文件
		log.Println(file.Filename)
		dst := fmt.Sprintf("E:/5120154230GoCode/src/gin_parambind/%s_%d", file.Filename, index)
		// 上传文件到指定的目录
		c.SaveUploadedFile(file, dst)
	}
	c.JSON(http.StatusOK, gin.H{
		"message": fmt.Sprintf("%d files uploaded!", len(files)),
	})
})

五、路由组

路由组主要是为了管理一些相同前缀的URL。

userGroup := r.Group("/user") //提取公共部分
{ //也可以不用括号包裹,包裹是为了规范
	userGroup.GET("/index", func(c *gin.Context) {})
	userGroup.GET("/login", func(c *gin.Context) {})
	userGroup.POST("/login", func(c *gin.Context) {})
}
// 嵌套路由组
shopGroup := r.Group("/shop")
{
	shopGroup.GET("/index", func(c *gin.Context) {})
	shopGroup.GET("/cart", func(c *gin.Context) {})
	shopGroup.POST("/checkout", func(c *gin.Context) {})
	// 嵌套路由组
	xx := shopGroup.Group("xx")
	xx.GET("/oo", func(c *gin.Context) {})
}

六、代码合集

int和string相互转化的方法:

int, err := strconv.Atoi(string) //string转int
int64, err := strconv.ParseInt(string, 10, 64)// string转int64,string为string参数,10表示10进制,64表示精度这里为int64
string := strconv.FormatInt(int64,10) // int64转string
string := strconv.Itoa(int) //int转string

下面就是一些练习的代码了:

package main

import (
	"net/http"

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

func main() {
	r := gin.Default() //返回一个默认的路由引擎
	r.GET("/hello", func(c *gin.Context) {
		//name := c.Query("name") 参数
		//name := c.DefaultQuery("name","dingli")
		//name, ok := c.GetQuery("name")
		//if !ok {
		//	name = "xxx" //取不到值
		//}

		c.String(200, "lifangding.com")
	})
	// 方法1:直接返回json或者使用map返回
	r.POST("/postRequest", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"method": "POST",
			"name":   "dingli",
		})
	})
	r.GET("/data", func(c *gin.Context) {
		data := map[string]interface{}{
			"name": "ding",
			"age":  23,
		}
		c.JSON(http.StatusOK, data)
	})
	// 方法二、结构体,注意字段首字母大写
	type Msg struct {
		Code int
		Msg  string
		Data interface{}
	}
	type IUser struct {
		Name string
		Age  int
		Sex  bool
	}

	r.GET("/hjson", func(c *gin.Context) {
		msg := Msg{200, "ding", IUser{
			Name: "dingli",
			Age:  26,
			Sex:  true,
		}}
		c.JSON(http.StatusOK, msg)
	})
	r.GET("/info", func(c *gin.Context) {
		var Info struct {
			Tel  int `json:"tel"` //当需要字段名为小写时候使用tag,表示使用json包操作该结构体的Tel字段为tel字段
			Addr string
		}
		Info.Tel = 17781863895
		Info.Addr = "四川泸州"
		c.JSON(http.StatusOK, Info) //反射操作
	})
	//响应HTML页面

	//r.Static("/static", "./static") //静态资源路径
	r.LoadHTMLFiles("./login.html", "./index.html")
	r.GET("/login", func(c *gin.Context) {
		c.HTML(http.StatusOK, "login.html", nil)
	})
	r.POST("/login", func(c *gin.Context) {
		//获取form表单中的数据,PostForm获取不到数据返回nil
		username := c.PostForm("username")
		password := c.PostForm("password")
		//username1, ok := c.GetPostForm("username") //通过ok判断是否获取到值
		//if !ok {
		//	username1 = "dingli"
		//}

		//username = c.DefaultPostForm("username","defaultValue") //设置默认值
		c.HTML(http.StatusOK, "index.html", gin.H{
			"Username": username,
			"Password": password,
		})
	})
	//获取URI路径参数 如:login/username/password
	r.GET("/:name/:age", func(c *gin.Context) {
		name := c.Param("name")
		age := c.Param("age") //string类型
		c.JSON(http.StatusOK, gin.H{
			"name": name,
			"age":  age,
		})
	})

	r.GET("/blog/:year/:month", func(c *gin.Context) {
		year := c.Param("year")
		month := c.Param("month")
		c.JSON(http.StatusOK, gin.H{
			"year":  year,
			"month": month,
		})
	})

	r.Run(":8080")
}

路由组、文件上传

package main

import (
	"fmt"
	"log"
	"net/http"

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

type UserInfo struct {
	Username string `form:"username" json:"username"` //设置form等方便进行参数绑定  binding:"required"
	Password string `form:"password" json:"password"`
}

/**
如果是 GET 请求,只使用 Form 绑定引擎(query)。
如果是 POST 请求,首先检查 content-type(shouldBind去识别三种form、querystring、json) 是否为 JSON 或 XML,然后再使用 Form(form-data)。
*/
func main() {
	r := gin.Default()
	r.POST("/user", func(c *gin.Context) {
		var userInfo UserInfo //go中都是值传递,因此需要改变传递地址
		// 1、绑定JSON的示例 ({"user": "q1mi", "password": "123456"})  //postman的body中选择raw写原始json数据
		//2、绑定form表单示例 (user=q1mi&password=123456)
		//3、绑定QueryString示例 (/loginQuery?user=q1mi&password=123456)
		if err := c.ShouldBind(&userInfo); err == nil {
			fmt.Printf("login info: %#v\n", userInfo)
			c.JSON(http.StatusOK, gin.H{
				"username": userInfo.Username,
				"password": userInfo.Password,
			})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})
	// 文件上传
	r.LoadHTMLFiles("./index.html")
	r.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", nil)
	})
	//处理multipart forms提交文件时默认的内存(缓冲区)限制是32MiB,超过了则会下载一个临时文件里,服务端则会从临时文件中读取刷盘
	r.MaxMultipartMemory = 8 << 20 //8MiB ,修改缓冲区大小
	//单个文件上传
	r.POST("/single-upload", func(c *gin.Context) {
		file, err := c.FormFile("f1")
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"error": err.Error(),
			})
			return
		}
		log.Println(file.Filename)
		dst := fmt.Sprintf("E:/5120154230GoCode/src/gin_parambind/%s", file.Filename) //路径需要存在
		//dst = path.Join("./file/",file.Filename) //两种方式均可得到路径
		err = c.SaveUploadedFile(file, dst) //上传文件到指定的目录
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"error": err.Error(),
			})
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("'%s' upload!", file.Filename),
		})
	})

	//多文件上传
	r.POST("/multi-upload", func(c *gin.Context) {
		// Multipart form
		form, _ := c.MultipartForm()
		files := form.File["file"]

		for index, file := range files {
			log.Println(file.Filename)
			dst := fmt.Sprintf("E:/5120154230GoCode/src/gin_parambind/%s_%d", file.Filename, index)
			// 上传文件到指定的目录
			c.SaveUploadedFile(file, dst)
		}
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("%d files uploaded!", len(files)),
		})
	})
	//HTTP的重定向
	r.GET("/testHttpRedirect", func(c *gin.Context) {
		c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")
	})
	//路由的重定向
	r.GET("/testA", func(c *gin.Context) {
		c.Request.URL.Path = "/testB"
		r.HandleContext(c)
	})
	r.GET("/testB", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "ok",
		})
	})
	// 默认无效请求处理
	r.NoRoute(func(c *gin.Context) {
		c.HTML(http.StatusNotFound, "404.html", nil)
	})

	userGroup := r.Group("/user") //提取公共部分
	{                             //也可以不用括号包裹
		userGroup.GET("/index", func(c *gin.Context) {})
		userGroup.GET("/login", func(c *gin.Context) {})
		userGroup.POST("/login", func(c *gin.Context) {})
	}
	// 嵌套路由组
	shopGroup := r.Group("/shop")
	{
		shopGroup.GET("/index", func(c *gin.Context) {})
		shopGroup.GET("/cart", func(c *gin.Context) {})
		shopGroup.POST("/checkout", func(c *gin.Context) {})
		// 嵌套路由组
		xx := shopGroup.Group("xx")
		xx.GET("/oo", func(c *gin.Context) {})
	}

	r.Run(":8081")
}

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgehgjg
系列文章
更多 icon
同类精品
更多 icon
继续加载