Go语言Gin框架基础

Go教程文档

何为gin

net/http

gin安装

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

入门案例

package main

import (
    "net/http"

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

func main() {
    // 1.创建路由
   router:=gin.Default()
   // 2.绑定路由规则,执行的函数
   // gin.Context,封装了request和response
   router.GET("/v1/topic", func(c *gin.Context) {
		if c.Query("username") ==""{
			c.String(http.StatusOK,"获取帖子列表")
		}else {
			username :=c.Query("username")
			fmt.Print("username= ",username)
			c.String(200,"获取用户名为%s的帖子的列表",c.Query("username"))
		}
	})
   // 3.监听端口,默认在8080
   // Run("里面不指定端口号默认为8080") 
   router.Run(":8000")
}

模块封装

在项目根目录下创建dao文件夹,将一些逻辑函数封装在这里!

创建TopicDao.go

package dao

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

/*获取帖子列表*/
func GetTopicDetail(c *gin.Context) {
	c.String(200,"获取topic id=%s的帖子",c.Param("topic_id"))
}

/*新增帖子*/
func AddTopic(c *gin.Context)  {
	c.String(200,"新增帖子!")
}

/*删除帖子*/
func DeleteTopic(c *gin.Context)  {
	c.String(200,"删除帖子!")
}

/*必须登录*/
func MustLogin() gin.HandlerFunc {
	return func(c *gin.Context) {
		//go语言中可以用下划线表示可忽略参数
		if _,status:=c.GetQuery("token");!status{
			c.String(401,"缺少token参数")
			c.Abort()
		}
	}
}

函数名首字母要大写,你不大写GoLang会强制纠正你!我们知道go语言里,定义函数的关键字是func,紧跟着是函数名,参数列表与返回值!

主函数里使用

	router:=gin.Default()
	v1 :=router.Group("/a1/topic")
	v1.GET("", func(c *gin.Context) {
		if c.Query("username")==""{
			c.String(200,"获取帖子列表")
		}else {
			c.String(200,"获取用户名为%s的帖子的列表",c.Query("username"))
		}
	})
	v1.GET("/:topic_id", GetTopicDetail)

	//使用将中间件添加到组中,请参阅GitHub中的示例代码。
	v1.Use(MustLogin())//dao层定义的返回值是HandlerFunc,所以这里必须要加括号

	v1.POST("", AddTopic)
	v1.DELETE("/:id", DeleteTopic)

	router.Run()

如果MustLogin函数执行不通过,那么下面的post和delete方法也不会再调用,因为被Abort了!

可以使用postman测试一下!

我这里呢就不再测试去掉Abort的结果了,有兴趣的伙伴可以试试,会调用post的方法哦!

我们再添加token参数,如下图:

结果响应成功!

实体封装

在项目根目录下创建model文件夹,在该文件夹下创建Topic.go

type Topic struct {
	/* Id */
	TopicId int `json:"topic_id"`

	/* 标题 */
	TopicTitle string `json:"topic_title"`
}

/* 创建Topic对象 */
func CreateTopic(id int, title string) Topic {
	return Topic{id,title}
}

这里的`是左上角Esc下面的那个键,输入法切换至英语

json:"topic_id"

dao层使用:

/*获取帖子列表*/
func GetTopicDetail(c *gin.Context) {
	//c.String(200,"获取topic id=%s的帖子",c.Param("topic_id"))
	c.JSON(200,Model.CreateTopic(101,"帖子标题"))
}

main函数调用:

v1.GET("/:topic_id", GetTopicDetail)
{
	topic_id: 101,
	topic_title: "帖子标题"
}

拓展:

/* 查询绑定 */
type TopicQuery struct {
	/**
		json:相当于绑定一个别名
		form:对应输入参数
		binding:required表示此参数必有,否则报错
	 */
	UserName string `json:"username" form:"username" binding:"required"`
	Page int `json:"page" form:"page"`
	Size int `json:"size" form:"size"`
}

dao层使用:

/*获取帖子列表*/
func GetTopicList(c *gin.Context)  {
	query:=Model.TopicQuery{}
	err:=c.BindQuery(&query)
	if err != nil {
		c.String(400,"参数错误%s",err.Error())
	}else {
		c.JSON(200,query)
	}
}

main函数使用:

router.GET("v2/topic/list", GetTopicList)

各位朋友可以逐次测试,去掉form,看看能不能接收到参数,下图是正常请求!

实体中UserName不能为空,若为空则报错!

内置验证器的使用

模型:

/*内置验证器的使用*/
type TopicPlus struct {
	/* Id */
	TopicId int `json:"topic_id"`

	/* 标题 */
	TopicTitle string `json:"topic_title" binding:"min=4,max=20"`

	/* 短标题  nefield不能与要求的字段重复 */
	TopicShortTitle string `json:"topic_s_title" binding:"required,nefield=TopicTitle"`

	/* 用户IP*/
	UserIP string `json:"ip" binding:"ipv4"`

	/* 分数 要大于5 */
	TopicScore int `json:"score" binding:"gt=5"`
}

binding里面有很多内置的方法,你只需要像Java那样去配置就可以

dao层:

/*新增帖子*/
func AddTopic(c *gin.Context)  {
	query:=Model.TopicPlus{}
	err:=c.BindJSON(&query) //绑定的是json
	if err != nil {
		c.String(400,"参数错误%s",err.Error())
	}else {
		c.JSON(200,query)
	}
}

然后启动测试:

上图是按照实体里面绑定的规则来写的参数,实际你们可以试着打破这个规则,系统会有非常完美的提示!

正则验证Json参数

先写个实体:

/*内置验证器的使用*/
type TopicPlus struct {
	/* Id */
	TopicId int `json:"topic_id"`

	/* 标题 */
	TopicTitle string `json:"topic_title" binding:"min=4,max=20"`

	/* 短标题  nefield不能与要求的字段重复 */
	TopicShortTitle string `json:"topic_s_title" binding:"required,nefield=TopicTitle"`

	/* 用户IP*/
	UserIP string `json:"ip" binding:"ipv4"`

	/* 分数 要大于5 */
	TopicScore int `json:"score" binding:"gt=5"`

	/* 链接 */
	TopicUrlParam string `json:"url" binding:"omitempty,topicUrl"`
}

关键在于binding:"omitempty,topicUrl",这里topicUrl要与验证规则进行绑定

新建validator包,定义规则匹配类

package validator

import (
	"github.com/go-playground/validator/v10"
	"regexp"
)

//这是v10的写法,v8需要参照上面的博客
func TopicUrl(fl validator.FieldLevel) bool {
	if  url,ok:=fl.Field().Interface().(string);ok{
		if matched,_ := regexp.MatchString(`\w{4,10}`,url); matched{
			return true
		}
	}
	return false
}

在main函数里,将上面两个配置进行关联:

	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		v.RegisterValidation("topicUrl", TopicUrl)
	}

以上是摘自官方和网络,有关自定义参数验证器的方法!

批量提交

相信不论是你学习什么语言,但凡提交必然会有批量提交的需求,Go同样不例外,那么Go的批量提交怎么去做呢?

Model:

/*批量新增*/
type Topics struct {
	/* 定义一个切片 gt 大于  lt 小于*/
	//dive是使Topic里面定义的规则生效!
	TopicList []Topic `json:"topics" binding:"gt=0,lt=3,dive"`
	TopicSize int `json:"size"`
}

dao:

/*批量新增帖子*/
func AddTopics(c *gin.Context)  {
	query:=Model.Topics{}
	err:=c.BindJSON(&query)
	if err != nil {
		c.String(400,"参数错误%s",err.Error())
	}else {
		c.JSON(200,query)
	}
}

main.go

	v2 := router.Group("/a2/topic")
	{
		v2.POST("", AddTopics)
	}

postman进行测验:

这样其实有一个问题,我们定义在Topic里面的参数约束会失效,那么我们怎么来解决呢?配置dive,他就会在添加的时候,对子层进行校验,如果子层的子层还有校验,同理添加!

Gorm简单入门

func main() {
	//连接数据库
	db, _ := gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/leotemp?charset=utf8&loc=Local")
	//一个坑,不设置这个参数,gorm会把表名转义后加个s,导致找不到数据库的表
	db.SingularTable(true)
	rows, _ := db.Raw("select id,className from class").Rows()
	for rows.Next(){
		var id int
		var name string
		rows.Scan(&id,&name)
		fmt.Println(id,name)
	}
	defer db.Close()
}

建表:

CREATE TABLE `class` (
  `id` int(10) NOT NULL COMMENT 'ID',
  `className` varchar(255) DEFAULT NULL COMMENT '班级名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

http://gorm.book.jasperxu.com/ 建议您在百忙中看完!只需一小时,过个大概!

DB封装

建表:

CREATE TABLE `topic_class` (
  `class_id` int(11) NOT NULL COMMENT 'id',
  `class_name` varchar(255) DEFAULT NULL COMMENT '名称',
  `class_remark` text CHARACTER SET utf8mb4 COMMENT '备注',
  PRIMARY KEY (`class_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

model:

type TopicClass struct {
	ClassId int `json:"class_id" gorm:"primary_key"`
	ClassName string `json:"class_name"`
	ClassRemark string `json:"class_remark"`
}

封装DB,先创建DataBase文件夹

package DataBase

import (
	"fmt"
	"github.com/jinzhu/gorm"
)
/**
封装数据库,需要完善!
 */
var DataHelper *gorm.DB
var err error
func init() {
	DataHelper,err = gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/leotemp?charset=utf8&loc=Local")
	if err != nil{
		fmt.Println(err)
		//defer db.Close()
	}
	//一个坑,不设置这个参数,gorm会把表名转义后加个s,导致找不到数据库的表
	DataHelper.SingularTable(true)
	DataHelper.LogMode(true)
}

dao:

/*获取帖子列表*/
func GetTopicDetail(c *gin.Context) {
	//c.String(200,"获取topic id=%s的帖子",c.Param("topic_id"))
	//c.JSON(200,Model.CreateTopic(101,"帖子标题"))
	tid := c.Param("topic_id")
	topics:=Model.TopicClass{}
	DataHelper.Find(&topics,tid)
	c.JSON(200,topics)
}

main:

v1 := router.Group("/a1/topic")
v1.GET("/:topic_id", GetTopicDetail)

上面是最基础的代码,但是他存在一个问题就是连接开启后没有关闭,如果你手动关闭的话,那么访问完之后想要再次访问就不行了!