Skip to content

Files

Latest commit

1c74697 · May 26, 2022

History

History
367 lines (285 loc) · 8.19 KB

6.1.md

File metadata and controls

367 lines (285 loc) · 8.19 KB

6.1 Go之web框架gin

前言

近来计划用go做一个项目,在选哪个web框架上比较纠结。go的web框架有很多,近几年流行的以性能见长的有gin、iris、echo等,经过查阅资料,及咨询从事go开发的同仁,最终选择用gin框架做。

主要基于以下几点:

  1. 人多占优(无论从使用人数还是参与维护的人数),这一点很重要。用的人多,不能证明他很好,但起码不差。维护的人多有利于版本的迭代,不至于给自己将来挖坑。
  2. 文档支持相对比较好,入手成本低。
  3. gin拥有详尽的出错信息,极为方便调试。
  4. 采用轻量级的中间件式框架,把网站变成只有api的一个个小服务。

官方地址:https://github.com/gin-gonic/gin 详细文档地址:https://godoc.org/github.com/gin-gonic/gin

安装

下载安装,安装环境 centos7

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

前提是你已经 安装go。

创建main.go 创建目录

mkdir $GOPATH/src/test
cd $GOPATH/src/test
vim main.go
package main

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

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

运行

# run example.go and visit 0.0.0.0:8080/ping on browser
$ go run example.go

访问 ip:8080/ping 8080为默认端口,你也可以通过 r.Run(端口号)设置。

路由

示例

package main

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

func main() {
	r := gin.Default()
	r.GET("/buildget", BuildGet)//BuildGet 方法,自己建
	r.POST("/buildpost", BuildPost)
	r.PUT("/buildput", BuildPut)
	r.DELETE("/builddelete", BuildDelete)
	r.PATCH("/buildpatch", BuildPatch)
	r.HEAD("/buildhead", BuildHead)
	r.OPTIONS("/buildoptions", BuildOptions)
	r.Run(":8282")
}

带参数的路由

示例

package main

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

func main() {
   r := gin.Default()
   //访问方式路由必须为/buildget/{参数} 不可以为/buildget/或/buildget
   r.GET("/buildget/:name", func(c *gin.Context) {
      name := c.Param("name") //通过Param获取
      c.String(http.StatusOK, "Hello world"+name)
   })
   //路由支持 /buildget/{参数}/ 或 /buildget/{参数}/{参数}
   r.GET("/buildget/:name/*action", func(c *gin.Context) {
      name := c.Param("name") //通过Param获取
      action := c.Param("action")
      c.String(http.StatusOK, "Hello world"+name+" "+action)
   })

   r.Run(":8282")
}

多路由

示例

package main

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

func main() {
   r := gin.Default()
   // Simple group: v1 访问方式 /v1/login
   v1 := r.Group("/v1")
   {
      v1.GET("/login", func(c *gin.Context) {
         c.String(http.StatusOK, "v1 login")
      })
   }
   // Simple group: v2  访问方式 /v2/login
   v2 := r.Group("/v2")
   {
      v2.GET("/login", func(c *gin.Context) {
         c.String(http.StatusOK, "v2 login")
      })
   }

   r.Run(":8282")
}

参数

获取get方式路由的参数

package main

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

func main() {
   r := gin.Default()
   //访问实例 /login?param1=a&param2=b&ids[1]=f&ids[2]=h
   r.GET("/login", func(c *gin.Context) {
      param1 := c.DefaultQuery("param1", "c") //设置初始值
      param2 := c.Query("param2")
      //param2 := c.Request.URL.Query().Get("param2")同上
      ids := c.QueryMap("ids")  //ids数组
      fmt.Println(ids)
      c.String(http.StatusOK, "Hello world "+param1+"_"+param2)
   })
   r.Run(":8282")
}

获取post方式路由的参数

package main

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

func main() {
	r := gin.Default()
	//访问实例 /login?param1=a&param2=b
	r.POST("/login", func(c *gin.Context) {
		param1 := c.DefaultPostForm("param1", "c") //设置初始值
		param2 := c.PostForm("param2")
		param3,has :=c.GetPostForm("param2")
		names := c.PostFormMap("names")//names 是数组
		if !has {

		}
		fmt.Println( param1,param2,param3,names)
	})
	r.Run(":8282")
}

上传单个文件

package main

import (
   "github.com/gin-gonic/gin"
   "path"
   "os"
   "io"
   "time"
)

func main() {
   r := gin.Default()
   //访问实例
   r.POST("/upload", func(c *gin.Context) {
      //处理上传文件
      //根据当前时间生成目录
      timestamp := time.Now().Unix()
      tm2 := time.Unix(timestamp, 0)
      //创建上传目录
      uploadDir := "/images/" + tm2.Format("20060102") + "/"
      os.MkdirAll(uploadDir, os.ModePerm)
      file, header, err := c.Request.FormFile("file")
      if err == nil {
         filename := header.Filename
         //扩展名
         ext := path.Ext(header.Filename)
         
         //生成文件名
         filefullname := uploadDir + filename + ext
         //fmt.Println(file, err, filename)
         out, err := os.Create(filefullname)
         defer out.Close()
         if err != nil {

         }
         io.Copy(out, file)
         
          //c.SaveUploadedFile(file,filefullname)
      }
   })
   r.Run(":8282")
}

上传多个文件

package main

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

func main() {
   r := gin.Default()
   // Set a lower memory limit for multipart forms (default is 32 MiB)
   r.MaxMultipartMemory = 8 << 20 // 8 MiB
   //访问实例
   r.POST("/upload", func(c *gin.Context) {
      //处理上传多个文件
      //获取上传的文件组
      form, _ := c.MultipartForm()
      files := form.File["upload[]"]
      for _, file := range files {
         log.Println(file.Filename)
         c.SaveUploadedFile(file,file.Filename)
      }

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

Bind

很多时候客户端和服务端采用json进行通信,即无论返回的response还是提交的request,其content-type类型都是application/json的格式。对于gin框架来说,我们就需要bind功能来解决这个问题。

示例

type User struct {
	Username string `form:"username" json:"username" binding:"required"`
	Password   string `form:"password" json:"password" binding:"required"`
}
func Login(c *gin.Context) {
	var u User
	err :=c.BindJSON(&u)
	fmt.Println(err)
	fmt.Println(u)
}

先建一个结构体user,再使用BindJSON绑定,将request中的Body中的数据按照JSON格式解析到u结构体中。

需要注意:

  • binding:"required" 字段对应的参数未必传没有会抛出错误,非banding的字段,对于客户端没有传,User结构会用零值填充。对于User结构没有的参数,会自动被忽略。
  • 结构体字段类型和所传参数类型要一致。

Bind的实现都在gin/binding里面. 这些内置的Bind都实现了Binding接口, 主要是Bind()函数.

  • context.BindJSON() 支持MIME为application/json的解析
  • context.BindXML() 支持MIME为application/xml的解析
  • context.BindYAML() 支持MIME为application/x-yaml的解析
  • context.BindQuery() 只支持QueryString的解析, 和Query()函数一样
  • context.BindUri() 只支持路由变量的解析
  • Context.Bind() 支持所有的类型的解析, 这个函数尽量还是少用(当QueryString, PostForm, 路由变量在一块同时使用时会产生意想不到的效果), 目前测试Bind不支持路由变量的解析, Bind()函数的解析比较复杂, 这部分代码后面再看

针对content-type类型是application/json的格式的数据,也可以通过以下方式获取。

示例

	jsonstr, _ := ioutil.ReadAll(c.Request.Body)
	fmt.Println(string(jsonstr))
	var data map[string]interface{}
	err := json.Unmarshal(jsonstr, &data)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(data)

获取header、body信息

		body,_ := ioutil.ReadAll(c.Request.Body)
		fmt.Println(string(body))

		for k,v :=range c.Request.Header {
			fmt.Println(k,v)
		}

获取ip

func GetClientIp(c *gin.Context) string{
	ip:=c.ClientIP()
	if ip=="::1" {
		ip="127.0.0.1"
	}
	return ip	
}

参考:

https://www.haohongfan.com/post/2019-01-21-gin-03/

http://www.okyes.me/2016/05/03/go-gin.html

https://www.jianshu.com/p/a31e4ee25305

links