镜缘浮影 小人本住在 苏州的城外 家里有屋又有田 生活乐无边

Gin 11

2018-07-11
wilmosfang
go
 
原文地址 http://soft.dog/2018/07/11/go-gin-11/

前言

Gin 是一款用 Go(Golang) 编写的 web 框架

Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to httprouter

因为 httprouter, 它提供了更高的性能

这里演示一下 Gin 的模型绑定和确认

使用 model binding 来将请求中的数据绑定到一个 model 中,形成一个结构体,Gin 目前支持绑定 JSON XML 和标准的表单数据 (foo=bar&boo=baz)

Gin 使用 go-playground/validator.v8 来对数据进行校验,详细标记可以参考 package validator

gin 的 API 可以参考 API REFFERENCE

Tip: 当前的版本为 Gin 1.2Go 1.10 (但是实验环境下,Go没有使用最新的版本)


操作

系统环境

[root@h160 ~]# hostnamectl 
   Static hostname: h160
         Icon name: computer-vm
           Chassis: vm
        Machine ID: d46f9440d4be429ea66b726977adf233
           Boot ID: a0fc6a1b4f124e39a27866d4df0701fa
    Virtualization: kvm
  Operating System: CentOS Linux 7 (Core)
       CPE OS Name: cpe:/o:centos:centos:7
            Kernel: Linux 3.10.0-862.2.3.el7.x86_64
      Architecture: x86-64
[root@h160 ~]# ip a 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:c9:c7:04 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global noprefixroute dynamic eth0
       valid_lft 84363sec preferred_lft 84363sec
    inet6 fe80::5054:ff:fec9:c704/64 scope link 
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:48:f4:2c brd ff:ff:ff:ff:ff:ff
    inet 192.168.56.160/24 brd 192.168.56.255 scope global noprefixroute eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe48:f42c/64 scope link 
       valid_lft forever preferred_lft forever
[root@h160 ~]# go version
go version go1.9.4 linux/amd64
[root@h160 ~]#

进入 GOPATH

[vagrant@h160 ~]$ echo $GOPATH
/vagrant/go
[vagrant@h160 ~]$ cd $GOPATH
[vagrant@h160 go]$ pwd
/vagrant/go
[vagrant@h160 go]$ 

代码注解

package main //指明此包为 main 包

import ( //使用这种方式导入多个包可以更为简洁
	"io"       //io 包可以提供基础的接口以进行I/O类操作
	"net/http" //http.StatusOK 需要用到这个包
	"os"       //os 包可以提供抽象的操作系统函数

	"github.com/gin-gonic/gin" //导入 gin 包
)

// 从 JSON 绑定数据
type Login struct { //构建一个为 Login 的结构体
	User     string `form:"user" json:"user" binding:"required"`         // 属性 User 为必要的,取自 json 类型数据,key 为 user
	Password string `form:"password" json:"password" binding:"required"` //属性 Password 为必要的,取自 json 类型数据, key 为 pass
}

func main() { // 一个main包中有且只有一个main 函数
	gin.DisableConsoleColor()                        //DisableConsoleColor 在console 中关闭彩色输出.
	f, _ := os.Create("gin.log")                     //func Create(name string) (*File, error) 接受一个文件名字符串,反馈一个文件指针,和一个错误输出
	gin.DefaultWriter = io.MultiWriter(f, os.Stdout) //将输出写出到文件与终端各一份
	r := gin.Default()                               // 使用 gin.Default() 方法生成一个引擎实例,这个实例默认情况下已经将 Logger Recovery 进行了装载
	r.POST("/loginJSON", func(c *gin.Context) {      //POST 是 router.Handle("POST", path, handle)的快捷方式    func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes
		var json Login                                  //设定一个 Login 类型的结构体 json
		if err := c.ShouldBindJSON(&json); err == nil { //判断 ShouldBindJSON 的结果 err 是否为nil,如果为nil 就执行 if 判断语句块,func (c *Context) ShouldBindJSON(obj interface{}) error ShouldBindJSON 是 c.ShouldBindWith(obj, binding.JSON) 的快捷方式
			if json.User == "manu" && json.Password == "123" { // 接着判断结构体 json 中 User 和 Password 是否为指定值
				c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) //反馈一个登录成功的 json 信息
			} else {
				c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) //否则反馈一个登录不成功的 json 信息
			}
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) //判断 ShouldBindJSON 的结果 err 不为nil,如果不为nil 就执行 else 判断语句块, 将错误提醒打印出来
		}
	})
	r.POST("/loginForm", func(c *gin.Context) { //POST 是 router.Handle("POST", path, handle)的快捷方式    func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes
		var form Login                              //设定一个 Login 类型的结构体 form
		if err := c.ShouldBind(&form); err == nil { //判断 ShouldBind 的结果 err 是否为nil,如果为nil 就执行 if 判断语句块   func (c *Context) ShouldBind(obj interface{}) error   ShouldBind checks the Content-Type to select a binding engine automatically, Depending the "Content-Type" header different bindings are used
			if form.User == "manu" && form.Password == "123" { // 接着判断结构体 form 中 User 和 Password 是否为指定值
				c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) //反馈一个登录成功的 json 信息
			} else {
				c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) //否则反馈一个登录不成功的 json 信息
			}
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) //判断 ShouldBind 的结果 err 不为nil,如果不为nil 就执行 else 判断语句块, 将错误提醒打印出来
		}
	})

	r.Run(":8080") //在 0.0.0.0:8080 上启监听
}

编译执行

[vagrant@h160 go]$ go run hello.go 
[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.

[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] POST   /loginJSON                --> main.main.func1 (3 handlers)
[GIN-debug] POST   /loginForm                --> main.main.func2 (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080
[GIN] 2018/07/11 - 16:14:57 | 400 |    1.487198ms |  192.168.56.105 | POST     /loginJSON
[GIN] 2018/07/11 - 16:15:12 | 200 |     193.097µs |  192.168.56.105 | POST     /loginJSON
[GIN] 2018/07/11 - 16:15:27 | 200 |     181.733µs |  192.168.56.105 | POST     /loginForm
[GIN] 2018/07/11 - 16:15:34 | 400 |     368.599µs |  192.168.56.105 | POST     /loginForm
[GIN] 2018/07/11 - 16:16:09 | 401 |     134.971µs |  192.168.56.105 | POST     /loginForm
[GIN] 2018/07/11 - 16:16:17 | 200 |     140.609µs |  192.168.56.105 | POST     /loginForm
[GIN] 2018/07/11 - 16:18:09 | 400 |     225.385µs |  192.168.56.105 | POST     /loginForm
...
...
...

客户的请求为

[vagrant@h105 ~]$ curl -v -X POST http://192.168.56.160:8080/loginJSON -H 'content-type: application/json' -d '{ "user": "manu" }'
* About to connect() to 192.168.56.160 port 8080 (#0)
*   Trying 192.168.56.160...
* Connected to 192.168.56.160 (192.168.56.160) port 8080 (#0)
> POST /loginJSON HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.56.160:8080
> Accept: */*
> content-type: application/json
> Content-Length: 18
> 
* upload completely sent off: 18 out of 18 bytes
< HTTP/1.1 400 Bad Request
< Content-Type: application/json; charset=utf-8
< Date: Wed, 11 Jul 2018 16:14:57 GMT
< Content-Length: 100
< 
* Connection #0 to host 192.168.56.160 left intact
{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"}[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ curl -v -X POST http://192.168.56.160:8080/loginJSON -H 'content-type: application/json' -d '{ "user": "manu" ,"password":"123"}'
* About to connect() to 192.168.56.160 port 8080 (#0)
*   Trying 192.168.56.160...
* Connected to 192.168.56.160 (192.168.56.160) port 8080 (#0)
> POST /loginJSON HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.56.160:8080
> Accept: */*
> content-type: application/json
> Content-Length: 35
> 
* upload completely sent off: 35 out of 35 bytes
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Wed, 11 Jul 2018 16:15:12 GMT
< Content-Length: 30
< 
* Connection #0 to host 192.168.56.160 left intact
{"status":"you are logged in"}[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ curl -v -X POST http://192.168.56.160:8080/loginForm -H 'content-type: application/json' -d '{ "user": "manu" ,"password":"123"}'
* About to connect() to 192.168.56.160 port 8080 (#0)
*   Trying 192.168.56.160...
* Connected to 192.168.56.160 (192.168.56.160) port 8080 (#0)
> POST /loginForm HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.56.160:8080
> Accept: */*
> content-type: application/json
> Content-Length: 35
> 
* upload completely sent off: 35 out of 35 bytes
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Wed, 11 Jul 2018 16:15:27 GMT
< Content-Length: 30
< 
* Connection #0 to host 192.168.56.160 left intact
{"status":"you are logged in"}[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ curl -v -X POST http://192.168.56.160:8080/loginForm -H 'content-type: text/plain' -d 'user=manu&password=123'
* About to connect() to 192.168.56.160 port 8080 (#0)
*   Trying 192.168.56.160...
* Connected to 192.168.56.160 (192.168.56.160) port 8080 (#0)
> POST /loginForm HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.56.160:8080
> Accept: */*
> content-type: text/plain
> Content-Length: 22
> 
* upload completely sent off: 22 out of 22 bytes
< HTTP/1.1 400 Bad Request
< Content-Type: application/json; charset=utf-8
< Date: Wed, 11 Jul 2018 16:15:34 GMT
< Content-Length: 182
< 
* Connection #0 to host 192.168.56.160 left intact
{"error":"Key: 'Login.User' Error:Field validation for 'User' failed on the 'required' tag\nKey: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"}[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ curl -v -X POST http://192.168.56.160:8080/loginForm -H 'content-type: application/json' -d '{"password":"123","user": "manu_test" }'
* About to connect() to 192.168.56.160 port 8080 (#0)
*   Trying 192.168.56.160...
* Connected to 192.168.56.160 (192.168.56.160) port 8080 (#0)
> POST /loginForm HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.56.160:8080
> Accept: */*
> content-type: application/json
> Content-Length: 39
> 
* upload completely sent off: 39 out of 39 bytes
< HTTP/1.1 401 Unauthorized
< Content-Type: application/json; charset=utf-8
< Date: Wed, 11 Jul 2018 16:16:09 GMT
< Content-Length: 25
< 
* Connection #0 to host 192.168.56.160 left intact
{"status":"unauthorized"}[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ curl -v -X POST http://192.168.56.160:8080/loginForm -H 'content-type: application/json' -d '{"password":"123","user": "manu" }'
* About to connect() to 192.168.56.160 port 8080 (#0)
*   Trying 192.168.56.160...
* Connected to 192.168.56.160 (192.168.56.160) port 8080 (#0)
> POST /loginForm HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.56.160:8080
> Accept: */*
> content-type: application/json
> Content-Length: 34
> 
* upload completely sent off: 34 out of 34 bytes
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Wed, 11 Jul 2018 16:16:17 GMT
< Content-Length: 30
< 
* Connection #0 to host 192.168.56.160 left intact
{"status":"you are logged in"}[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ curl -v -X POST http://192.168.56.160:8080/loginForm -H 'content-type: application/xml' -d '<user>manu</user><password>123</password>'
* About to connect() to 192.168.56.160 port 8080 (#0)
*   Trying 192.168.56.160...
* Connected to 192.168.56.160 (192.168.56.160) port 8080 (#0)
> POST /loginForm HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.56.160:8080
> Accept: */*
> content-type: application/xml
> Content-Length: 41
> 
* upload completely sent off: 41 out of 41 bytes
< HTTP/1.1 400 Bad Request
< Content-Type: application/json; charset=utf-8
< Date: Wed, 11 Jul 2018 16:18:09 GMT
< Content-Length: 182
< 
* Connection #0 to host 192.168.56.160 left intact
{"error":"Key: 'Login.User' Error:Field validation for 'User' failed on the 'required' tag\nKey: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"}[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ curl -v -X POST http://192.168.56.160:8080/loginForm --form user=manu --form password=123
* About to connect() to 192.168.56.160 port 8080 (#0)
*   Trying 192.168.56.160...
* Connected to 192.168.56.160 (192.168.56.160) port 8080 (#0)
> POST /loginForm HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.56.160:8080
> Accept: */*
> Content-Length: 243
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=----------------------------bc8cf04c4143
> 
< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Thu, 19 Jul 2018 03:26:39 GMT
< Content-Length: 30
< 
* Connection #0 to host 192.168.56.160 left intact
{"status":"you are logged in"}[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ 
[vagrant@h105 ~]$ 

结果同时输出到了客户端和console

服务端的目标路径里也多了一个日志文件 gin.log,并且创建时间也是吻合的

[vagrant@h160 go]$ ls
bin  gin.log  hello.go  pkg  src
[vagrant@h160 go]$ cat gin.log 
[GIN] 2018/07/11 - 16:14:57 | 400 |    1.487198ms |  192.168.56.105 | POST     /loginJSON
[GIN] 2018/07/11 - 16:15:12 | 200 |     193.097µs |  192.168.56.105 | POST     /loginJSON
[GIN] 2018/07/11 - 16:15:27 | 200 |     181.733µs |  192.168.56.105 | POST     /loginForm
[GIN] 2018/07/11 - 16:15:34 | 400 |     368.599µs |  192.168.56.105 | POST     /loginForm
[GIN] 2018/07/11 - 16:16:09 | 401 |     134.971µs |  192.168.56.105 | POST     /loginForm
[GIN] 2018/07/11 - 16:16:17 | 200 |     140.609µs |  192.168.56.105 | POST     /loginForm
[GIN] 2018/07/11 - 16:18:09 | 400 |     225.385µs |  192.168.56.105 | POST     /loginForm
[vagrant@h160 go]$ 

内容与 console 中的输出是一样的,如果通过终端查看,客户访问的过程中,可以发现一前一后的实时记录变化


总结

构建一个简单的 web 还是非常快捷的

可以通过 Bind 的方法初始化 module


原文地址 http://soft.dog/2018/07/11/go-gin-11/

类似博客

评论