go json 数组表单提交_Go语言基础(十八)

d90c6f5d2d78f6123977decb38059a6c.png

Go语言基础(十八)

一、gin介绍

二、gin路由

三、gin数据解析与绑定

四、gin渲染

五、gin中间件


一、gin介绍

Go 语言最流行了两个轻量级 Web 框架分别是 Gin 和 Echo。gin是一个golang的微框架,封装比较优雅,API友好,源码注释明确。gin框架是Go语言进行web开发(api开发,微服务开发)框架中。

1、gin安装

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

2、hello world

package main

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

func main() {
	// 1创建路由
	r := gin.Default()
	// 2绑定路由规则
	r.GET("/", func(c *gin.Context) {
		c.String(http.StatusOK, "hello world")
	})
	// 3监听端口,默认8000
	r.Run(":8000")
}

在浏览器打开http://localhost:8000

ee650c6c42859a193e169746b7301d55.png

二、gin路由

gin框架采用的路由是基于httprouter做的,gin支持Restful风格的API。

1、通过Context的Param方法获取API参数

func main() {
	// 1创建路由,默认使用了两个中间件Logger(),Recovery()
	// 
	r := gin.Default()
	// 2绑定路由规则,
	// gin.Context,封装了request和respose
	r.GET("/welcome/:name/*action", func(c *gin.Context) {
		name := c.Param("name")
		action := c.Param("action")
		c.String(http.StatusOK, name + "is" + action)
	})
	// 3监听端口,默认8000
	r.Run(":8000")
}

2、通过DefaultQuery()或Query方法获取URL参数

	r.GET("/user/:name/*action", func(c *gin.Context) {
		name := c.DefaultQuery("name", "jack")
		c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
	})

91dd2094ffa9648436a43bb2d2988d19.png

3、通过PostForm()方法获取表单参数

首先创建一个html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
    <form action="http://127.0.0.1:8000/form" method="post" enctype="application/x-www-form-urlencoded">
        用户名:<input type="text" name="username">
        <br>
        密&nbsp&nbsp码:<input type="password" name="password">
        兴&nbsp&nbsp趣:
        <input type="checkbox" value="run" name="hobby">跑步
        <input type="checkbox" value="game" name="hobby">游戏
        <input type="checkbox" value="money" name="hobby">金钱
        <br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

直接run起来,然后通过PostForm()方法获取表单参数:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

func getting(c *gin.Context) {

}

func main() {
	// 1创建路由,默认使用了两个中间件Logger(),Recovery()
	// 
	r := gin.Default()
	// 2绑定路由规则,
	// gin.Context,封装了request和respose
	r.POST("/form", func(c *gin.Context) {
		typeStr := c.DefaultQuery("type", "alert")
		username := c.PostForm("username")
		password := c.PostForm("password")
		// 多选框
		hobbys := c.PostFormArray("hobby")
		c.String(http.StatusOK, fmt.Sprintf("type is %s, username:%s, password:%s, hooby:%v",
			typeStr, username, password, hobbys))
	})
	// 3监听端口,默认8000
	r.Run(":8000")
}

1fa1e40ddc065eb9e462dbc687aa6b4e.png

f6093dec3025816c3333727cccb44bd7.png

4、上传文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
    <form action="http://127.0.0.1:8000/upload" method="post" enctype="multipart/form-data">
        头像:
        <input type="file" name="file">
        <br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

在浏览器中打开,然后运行:

package main

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


func main() {
	// 1创建路由,默认使用了两个中间件Logger(),Recovery()
	// 
	r := gin.Default()
	// 2绑定路由规则,
	// gin.Context,封装了request和respose
	r.POST("/upload", func(c *gin.Context) {
		file, _ := c.FormFile("file")
		log.Println(file.Filename)
		c.SaveUploadedFile(file, file.Filename)
		c.String(200, fmt.Sprintf("%s upload file!", file.Filename))
	})
	// 3监听端口,默认8000
	r.Run(":8000")
}

e68c9a9d8cfae81f0f109990c793322e.png

835624d2f4598f83cb84f01bd42ad56c.png

5、routes group

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

package main

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

func main() {
	// 1创建路由
	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 %sn", name))
}

func submit(c *gin.Context) {
	name := c.DefaultQuery("name", "tom")
	c.String(200, fmt.Sprintf("hello %sn", name))
}

6c2c7918904994b440b0e4e8dee82d3b.png

6、路由原理

httproter会将所有的路由规则构造一颗前缀树。

三、gin数据解析和绑定

1、json数据解析和绑定

客户端传参,后端接收并解析得到结构体。

package main

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

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

func main() {
	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.Passward != "admin" {
			c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
			return
		}
		c.JSON(http.StatusBadRequest, gin.H{"status": "200"})
	})
	r.Run(":8000")
}

02e653b7e895ff2d2f1a745edc2396e2.png

2、表单数据绑定与解析

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<form action="http://127.0.0.1:8000/loginForm" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username">
    <br>
    密&nbsp&nbsp码:<input type="password", name="password">
    <input type="submit", value="登录">
</form>
</body>
</html>

然后执行:

package main

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

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

func main() {
	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.Passward != "admin" {
			c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
			return
		}
		c.JSON(http.StatusBadRequest, gin.H{"status": "200"})

	})
	r.Run(":8000")
}

4b131b3a81a3c20e217caa9e11df2fbf.png

3、URL数据绑定与解析

package main

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

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

func main() {
	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.Passward != "admin" {
			c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
			return
		}
		c.JSON(http.StatusBadRequest, gin.H{"status": "200"})

	})
	r.Run(":8000")
}

d32d6eca5c519cf77c6eccea513a70c2.png

四、gin渲染

1、各种数据格式的响应

package main

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

// 多种响应方式
func main() {
	r := gin.Default()
	// 1、json
	r.GET("/someJson", func(c *gin.Context) {
		c.JSON(200, gin.H{"message":"someJson", "status":200})
	})
	// 2、结构体响应
	r.GET("/someStruct", func(c *gin.Context) {
		var msg struct {
			Name string
			Message string
			Memner int
		}
		msg.Name = "root"
		msg.Message = "message"
		msg.Memner = 123
		c.JSON(200, msg)
	})
	// 3、xml
	r.GET("/someXml", func(c *gin.Context) {
		c.XML(200, gin.H{"message":"abc"})
	})
	// 4、YAML
	r.GET("/someYaml", func(c *gin.Context) {
		c.YAML(200, gin.H{"name":"zhangsan"})
	})
	// 5、protobuf格式,谷歌开发的高效存储读取的工具
	r.GET("/protobuf", func(c *gin.Context) {
		reps := []int64{int64(1), int64(2)}
		// 定义数据
		label := "label"
		// 传protobuf数据
		data := &protoexample.Test{
			Label: &label,
			Reps: reps,
		}
		c.ProtoBuf(200, data)
	})
	r.Run(":8000")
}

0c09bb752c45be5c528919dc39e1bdd5.png
json响应

832a70a64e835baef10d43be19342454.png
struct响应

820a9e4776388eb9177bc6c3813b2b0f.png
xml响应

b45d5391eb89666226c2a99f6a38e669.png
yaml响应

fdb0c680c300d2bfad16fccfe19a3fa2.png
protobuf响应

98672b73073d7ff2554db518a06d7ce1.png

2、HTML模板渲染

gin支持加载HTML模板,然后根据响应的模板参数进行配置并返回响应的数据,本质上就是字符串替换。LoadHTMLGlob()方法可以加载模板文件。

index.tmpl

<html>
    <h1>
        {{.title}}
    </h1>
</html>

使用HTML模板进行渲染:

package main

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

// HTML渲染
func main() {
	r := gin.Default()
	// 加载模板文件
	r.LoadHTMLGlob("templates/*")
	// 或者
	// r.LoadHTMLFiles("templates/index.tmpl")
	r.GET("/index", func(c *gin.Context) {
		// 根据文件名渲染
		// 最终json将title替换
		c.HTML(200, "index.tmpl", gin.H{"title":"我的标题"})
	})
	r.Run(":8000")
}

bf5b7197d614f195a27f307c6672bd7a.png

3、重定向

package main

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

func main() {
	r := gin.Default()
	r.GET("/redirect", func(c *gin.Context) {
		// 支持内部和外部重定向
		c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com/")
	})
	r.Run(":8000")
}

4、同步异步

goroutine可以很方便地实现异步处理,另外,在启动goroutine时,不应该使用原始上下文,必须使用它的只读副本。

package main

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

func main() {
	r := gin.Default()
	r.GET("/long_async", func(c *gin.Context) {
		// 需要一个副本
		copyContext := c.Copy()
		// 实现异步处理
		go func() {
			time.Sleep(3 * time.Second)
			log.Printf("异步执行:" + copyContext.Request.URL.Path)
		}()
	})

	// 同步
	r.GET("/long_sync", func(c *gin.Context) {
		time.Sleep(3 * time.Second)
		log.Printf("同步执行:" + c.Request.URL.Path)
	})
	r.Run(":8000")
}

五、gin中间件

  • gin可以构建中间件,但是它只对注册过的路由函数起作用
  • 对于分组路由,嵌套使用中间件
  • 中间件分为全局中间件、单个路由中间件和群组中间件
  • gin中间件必须是一个gin HandlerFunc类型

1、全局中间件

package main

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

// 定义中间件
func MiddleWare() gin.HandlerFunc {
	return func(c *gin.Context) {
		t := time.Now()
		fmt.Println("中间件开始执行了")
		// 设置变量到Context的key中,可以通过Get()获取
		c.Set("request", "中间件")
		// 执行中间件
		c.Next()
		status := c.Writer.Status()
		fmt.Println("中间件执行完毕", status)
		t2 := time.Since(t)
		fmt.Println("time", t2)
	}
}


func main() {
	// 1创建路由
	r := gin.Default()
	// 2注册中间件
	r.Use(MiddleWare())
	{
		// 同步
		r.GET("/middleware", func(c *gin.Context) {
			// 取值
			req, _ := c.Get("request")
			fmt.Println("request", req)
			// 页面接收
			c.JSON(200, gin.H{"request":req})
		})
	}


	r.Run(":8000")
}

da9fe7fe32c4a043a8a9fa6a47be72ec.png

0101920e22b9d3bcefb5f14bb2f926ad.png

2、局部中间件

		// 局部中间件
		r.GET("/middleware2", MiddleWare(), func(c *gin.Context) {
			// 取值
			req, _ := c.Get("request")
			fmt.Println("request", req)
			// 页面接收
			c.JSON(200, gin.H{"request":req})
		})

3、中间件小练习

定义程序计时中间件,然后定义2个路由,执行函数后打印统计时间。

package main

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

func myTime(c *gin.Context) {
	start := time.Now()
	c.Next()
	// 统计时间
	end := time.Since(start)
	fmt.Println("程序用时:", end)
}


func main() {
	// 1创建路由
	r := gin.Default()
	// 2注册中间件
	r.Use(myTime)
	shoppingGroup := r.Group("/shopping")
	{
		shoppingGroup.GET("/index", shopIndexHandler)
		shoppingGroup.GET("/home", shopHomeHandler)
	}
	r.Run(":8000")
}

func shopIndexHandler(c *gin.Context) {
	time.Sleep(time.Second * 5)
}

func shopHomeHandler(c *gin.Context) {
	time.Sleep(time.Second * 3)
}

bbf1a84cb6afdbda4905ce94db8e59bb.png

我是尾巴~

每日一句毒鸡汤:有时候就是这样,嘴上说了不可能,但心里却早已想好了千万种可能!

本次推荐:PDF全文翻译网站

Free Online Document Translator - Preserves your document's layout (Word, PDF, Excel, Powerpoint, OpenOffice, text)​www.onlinedoctranslator.com

继续加油~!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值