gin 快速入门手册

文章目录

  • 安装
  • URL和路由分组
    • 2. 带参数的url
    • 3. 获取路由分组的参数
  • 获取参数
    • 1. 获取get参数
    • 2. 获取post参数
    • 3. get、post混合
  • JSON 、 ProtoBuf渲染
    • 1. 输出json和protobuf
    • 2. PureJSON
  • 表单验证
    • 1. 表单的基本验证
  • 中间件和next函数
    • 1. 无中间件启动
    • 2. 使用中间件
    • 3. 自定义组件
  • 设置静态文件路径和HTML文件
    • 1. 设置静态文件路径
    • 2. index.html内容
    • 3. templates/posts/index.tmpl
    • 4. templates/users/index.tmpl
  • 优雅重启或停止
  • gorm
    • 1. 什么是orm
    • 2. 常用orm
    • 3. orm的优缺点
    • 4. 如何正确看待orm和sql之间的关系

官方手册

安装

go get -u github.com/gin-gonic/gin 
  1. 代码
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
}
  1. 使用get、post、put等http方法
func main() {
	// 使⽤默认中间件创建⼀个gin路由器
	// logger and recovery (crash-free) 中间件
	router := gin.Default()
	router.GET("/someGet", getting)
	router.POST("/somePost", posting)
	router.PUT("/somePut", putting)
	router.DELETE("/someDelete", deleting)
	router.PATCH("/somePatch", patching)
	router.HEAD("/someHead", head)
	router.OPTIONS("/someOptions", options)
	// 默认启动的是 8080端⼝,也可以⾃⼰定义启动端⼝
	router.Run()
	// router.Run(":3000") for a hard coded port
}

URL和路由分组

  1. 路由分组
func main() {
	router := gin.Default()
	// Simple group: v1
	v1 := router.Group("/v1")
	{
		v1.POST("/login", loginEndpoint)
		v1.POST("/submit", submitEndpoint)
		v1.POST("/read", readEndpoint)
	}
	// Simple group: v2
	v2 := router.Group("/v2")
	{
		v2.POST("/login", loginEndpoint)
		v2.POST("/submit", submitEndpoint)
		v2.POST("/read", readEndpoint)
	}
	router.Run(":8082")
}

2. 带参数的url

package main
	import (
		"github.com/gin-gonic/gin"
		"net/http"
	)
	func main() {
		r := gin.Default()
		r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
		"message": "pong",
		})
	})
	r.GET("/user/:name/:action/", func(c *gin.Context) {
		name := c.Param("name")
		action := c.Param("action")
		c.String(http.StatusOK, "%s is %s", name, action)
	})
	r.GET("/user/:name/*action", func(c *gin.Context) {
		name := c.Param("name")
		action := c.Param("action")
		c.String(http.StatusOK, "%s is %s", name, action)
	})
	r.Run(":8082")
}

3. 获取路由分组的参数

package main
	import "github.com/gin-gonic/gin"
	type Person struct {
		ID string `uri:"id" binding:"required,uuid"`
		Name string `uri:"name" binding:"required"`
	}
	func main() {
		route := gin.Default()
		route.GET("/:name/:id", func(c *gin.Context) {
		var person Person
		if err := c.ShouldBindUri(&person); err != nil {
		c.JSON(400, gin.H{"msg": err})
		return
	}
	c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
	})
	route.Run(":8088")
}

获取参数

1. 获取get参数

func main() {
	router := gin.Default()
	// 匹配的url格式: /welcome?firstname=Jane&lastname=Doe
	router.GET("/welcome", func(c *gin.Context) {
	firstname := c.DefaultQuery("firstname", "Guest")
	lastname := c.Query("lastname") // 是 c.Request.URL.Query().Get("lastname
	c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
	})
	router.Run(":8080")
}

2. 获取post参数

func main() {
	router := gin.Default()
	router.POST("/form_post", func(c *gin.Context) {
	message := c.PostForm("message")
	nick := c.DefaultPostForm("nick", "anonymous") // 此⽅法可以设置默认值
	c.JSON(200, gin.H{
		"status": "posted",
		"message": message,
		"nick": nick,
		})
	})
	router.Run(":8080")
}

3. get、post混合

POST /post?id=1234&page=1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=manu&message=this_is_great

func main() {
	router := gin.Default()
	router.POST("/post", func(c *gin.Context) {
	id := c.Query("id")
	page := c.DefaultQuery("page", "0")
	name := c.PostForm("name")
	message := c.PostForm("message")
	fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, mes
	})
	router.Run(":8080")
}

JSON 、 ProtoBuf渲染

1. 输出json和protobuf

新建user.proto文件

syntax = "proto3";
option go_package = ".;proto";
message Teacher {
	string name = 1;
	repeated string course = 2;
}
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"start/gin_t/proto"
)
func main() {
	r := gin.Default()
	// gin.H is a shortcut for map[string]interface{}
	r.GET("/someJSON", func(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
	})
	r.GET("/moreJSON", func(c *gin.Context) {
	// You also can use a struct
		var msg struct {
		Name string `json:"user"`
		Message string
		Number int
	}
	msg.Name = "Lena"
	msg.Message = "hey"
	msg.Number = 123
	// Note that msg.Name becomes "user" in the JSON
	// Will output : {"user": "Lena", "Message": "hey", "Number": 123}
	c.JSON(http.StatusOK, msg)
	})
	r.GET("/someProtoBuf", func(c *gin.Context) {
		courses := []string{"python", "django", "go"}
		// The specific definition of protobuf is written in the testdata/protoex
		Name: "bobby",
		Course: courses,
	}
	// Note that data becomes binary data in the response
	// Will output protoexample.Test protobuf serialized data
	c.ProtoBuf(http.StatusOK, data)
	})
	// Listen and serve on 0.0.0.0:8080
	r.Run(":8083")
}

2. PureJSON

通常情况下,JSON会将特殊的HTML字符替换为对应的unicode字符,比如 < 替换为 \u003c ,如果想原样输出html,则使用PureJSON

func main() {
	r := gin.Default()
	// Serves unicode entities
	r.GET("/json", func(c *gin.Context) {
	c.JSON(200, gin.H{
		"html": "Hello, world!",
		})
	})
	// Serves literal characters
	r.GET("/purejson", func(c *gin.Context) {
		c.PureJSON(200, gin.H{
		"html": "Hello, world!",
		})
	})
	// listen and serve on 0.0.0.0:8080
	r.Run(":8080")
}

表单验证

1. 表单的基本验证

若要将请求主体绑定到结构体中,请使用模型绑定,目前支持JSON、XML、YAML和标准表单值(foo=bar&boo=baz)的绑定。 Gin使用 go-playground/validator 验证参数

需要在绑定的字段上设置tag,比如,绑定格式为json,需要这样设置 json:“fieldname” 。
此外,Gin还提供了两套绑定方法:

  • Must bind
    Methods - Bind , BindJSON , BindXML , BindQuery , BindYAML Behavior - 这些方法底层使用 MustBindWith ,如果存在绑定错误,请求将被以下指令中止 c. AbortWithError(400,err).SetType(ErrorTypeBind) ,响应状态代码会被设置为400,请求头 Content-Type 被设置为 text/plain; charset=utf-8 。注意,如果你试图在此之后设置响应代码,将会发出一个警告 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422 ,如果你希望更好地控制行为,请使用 ShouldBind 相关的方法
  • Should bind
    Methods - ShouldBind , ShouldBindJSON , ShouldBindXML , ShouldBindQuery , ShouldBindYAML
    Behavior - 这些方法底层使用 ShouldBindWith ,如果存在绑定错误,则返回错误,开发人员可以正确处理请求和错误。

当我们使用绑定方法时,Gin会根据Content-Type推断出使用哪种绑定器,如果你确定你绑定的是什么,你可以使用 MustBindWith 或者 BindingWith 。你还可以给字段指定特定规则的修饰符,如果一个字段用 binding:“required” 修饰,并且在绑定时该字段的值为空,那么将返回一个错误。

// 绑定为json
type Login struct {
	User string `form:"user" json:"user" xml:"user" binding:"required"`
	Password string `form:"password" json:"password" xml:"password" binding:"requ`
}

type SignUpParam struct {
	Age uint8 `json:"age" binding:"gte=1,lte=130"`
	Name string `json:"name" binding:"required"`
	Email string `json:"email" binding:"required,email"`
	Password string `json:"password" binding:"required"`
	RePassword string `json:"re_password" binding:"required,eqfield=Password"`
}
func main() {
	router := gin.Default()
	// Example for binding JSON ({"user": "manu", "password": "123"})
	router.POST("/loginJSON", func(c *gin.Context) {
	var json Login
	if err := c.ShouldBindJSON(&json); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if json.User != "manu" || json.Password != "123" {
		c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
	})
	// Example for binding a HTML form (user=manu&password=123)
	router.POST("/loginForm", func(c *gin.Context) {
	var form Login
	// This will infer what binder to use depending on the content-type heade
	if err := c.ShouldBind(&form); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if form.User != "manu" || form.Password != "123" {
		c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
	})
	r.POST("/signup", func(c *gin.Context) {
	var u SignUpParam
	if err := c.ShouldBind(&u); err != nil {
		c.JSON(http.StatusOK, gin.H{
		"msg": err.Error(),
	})
		return
	}
	// 保存⼊库等业务逻辑代码...
	c.JSON(http.StatusOK, "success")
	})
	// Listen and serve on 0.0.0.0:8080
	router.Run(":8080")
}

中间件和next函数

1. 无中间件启动

#使⽤
r := gin.New()
#替代
// 默认启动⽅式,包含 Logger、Recovery 中间件
r := gin.Default()

2. 使用中间件

func main() {
// 创建⼀个不包含中间件的路由器
	r := gin.New()
	// 全局中间件
	// 使⽤ Logger 中间件
	r.Use(gin.Logger())
	// 使⽤ Recovery 中间件
	r.Use(gin.Recovery())
	// 路由添加中间件,可以添加任意多个
	r.GET("/benchmark", MyBenchLogger(), benchEndpoint)
	// 路由组中添加中间件
	// authorized := r.Group("/", AuthRequired())
	// exactly the same as:
	authorized := r.Group("/")
	// per group middleware! in this case we use the custom created
	// AuthRequired() middleware just in the "authorized" group.
	authorized.Use(AuthRequired())
	{
		authorized.POST("/login", loginEndpoint)
		authorized.POST("/submit", submitEndpoint)
		authorized.POST("/read", readEndpoint)
		// nested group
		testing := authorized.Group("testing")
		testing.GET("/analytics", analyticsEndpoint)
	}
	// Listen and serve on 0.0.0.0:8080
	r.Run(":8080")
}

3. 自定义组件

func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		t := time.Now()
		// Set example variable
		c.Set("example", "12345")
		// before request
		c.Next()
		// after request
		latency := time.Since(t)
		log.Print(latency)
		// access the status we are sending
		status := c.Writer.Status()
		log.Println(status)
		}
	}
	func main() {
		r := gin.New()
		r.Use(Logger())
		r.GET("/test", func(c *gin.Context) {
		example := c.MustGet("example").(string)
		// it would print: "12345"
		log.Println(example)
	})
	// Listen and serve on 0.0.0.0:8080
	r.Run(":8080")
}

设置静态文件路径和HTML文件

1. 设置静态文件路径

package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
	// 创建⼀个默认的路由引擎
	r := gin.Default()
	// 配置模板
	r.LoadHTMLGlob("templates/**/*")
	//router.LoadHTMLFiles("templates/template1.html", "templates/template2.html
	// 配置静态⽂件夹路径 第⼀个参数是api,第⼆个是⽂件夹路径
	r.StaticFS("/static", http.Dir("./static"))
	// GET:请求⽅式;/hello:请求的路径
	// 当客户端以GET⽅法请求/hello路径时,会执⾏后⾯的匿名函数
	r.GET("/posts/index", func(c *gin.Context) {
	// c.JSON:返回JSON格式的数据
	c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
		"title": "posts/index",
		})
	})
	r.GET("gets/login", func(c *gin.Context) {
		c.HTML(http.StatusOK, "posts/login.tmpl", gin.H{
		"title": "gets/login",
		})
	})
	// 启动HTTP服务,默认在0.0.0.0:8080启动服务
	r.Run()
}

2. index.html内容

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

3. templates/posts/index.tmpl

{{ define "posts/index.tmpl" }}
	<html><h1>
		{{ .title }}
	</h1>
	<p>Using posts/index.tmpl</p>
	</html>
{{ end }}

4. templates/users/index.tmpl

{{ define "users/index.tmpl" }}
<html><h1>
	{{ .title }}
</h1>
	<p>Using users/index.tmpl</p>
</html>
{{ end }}

优雅重启或停止

package main
import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
	"github.com/gin-gonic/gin"
)
func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
	time.Sleep(5 * time.Second)
	c.String(http.StatusOK, "Welcome Gin Server")
	})
srv := &http.Server{
	Addr: ":8080",
	Handler: router,
}
go func() {
	// service connections
	if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed
	log.Fatalf("listen: %s\n", err)
	}
}()
	// Wait for interrupt signal to gracefully shutdown the server with
	// a timeout of 5 seconds.
	quit := make(chan os.Signal)
	// kill (no param) default send syscanll.SIGTERM
	// kill -2 is syscall.SIGINT
	// kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	log.Println("Shutdown Server ...")
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
	log.Fatal("Server Shutdown:", err)
select {
	case <-ctx.Done():
	log.Println("timeout of 5 seconds.")
	}
	log.Println("Server exiting")
}

gorm

1. 什么是orm

ORM全称是:Object Relational Mapping(对象关系映射),其主要作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来。举例来说就是,我定义一个对象,那就对应着一张表,这个对象的实例,就对应着表中的一条记录。

对于数据来说,最重要最常用的是表:表中有列, orm就是将一张表映射成一个类,表中的列映射成类中的一个类。java 、python,但是针对go语言而言,struct,就是列如何映射,是因为列可以映射成struct中的类型,int->int,但是有另一个问题? 就是数据库中的列具备很好的描述性,但是struct有tag。执行sql, 需要我们有足够的sql语句基础、需要我们懂得不同的数据的sql

2. 常用orm

个人而言,不用太去纠结应该选择哪一个orm框架,但是实际上你用熟悉了一个,其他的orm迁移成本很低,我们选个一个star数量最高的一定不会有错,这些差异也不会很大
sql语言远比orm重要的多
https://github.com/go-gorm/gorm
https://github.com/facebook/ent
https://github.com/jmoiron/sqlx
https://gitea.com/xorm/xorm/src/branch/master/README_CN.md
https://github.com/didi/gendry/blob/master/translation/zhcn/README.md

3. orm的优缺点

优点:

  1. 提高了开发效率。
  2. 屏蔽sql细节。可以自动对实体Entity对象与数据库中的Table进行字段与属性的映射;不用直接SQL编码
  3. 屏蔽各种数据库之间的差异
    缺点:
  4. orm会牺牲程序的执行效率和会固定思维模式
  5. 太过依赖orm会导致sql理解不够
  6. 对于固定的orm依赖过重,导致切换到其他的orm代价高

4. 如何正确看待orm和sql之间的关系

  1. sql为主,orm为辅
  2. orm主要目的是为了增加代码可维护性和开发效率

一定要学好:

  1. group by
  2. 子查询
  3. having子句

你可能感兴趣的:(Go,gin)