Gin框架的使用

Gin的基本使用官方网址
golang databasesql官方网址
sqlx网址
go-redis网址
官方推荐redigo网址

一、多种请求类型

go遵循RESTful架构风格,所以在请求上有多种请求类型,下面是各种请求方式代表的含义

conn := gin.Default()
//请求所有文章
conn.GET("/actives", func(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"message": "get",
	})
})
//创建文章
conn.POST("/actives", func(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"message": "post",
	})
})
//修改文章
conn.PUT("/actives/:id", func(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"message": "put",
	})
})
//删除文章
conn.DELETE("/actives/:id", func(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"message": "delete",
	})
})
conn.Run()

二、加载静态资源

在Gin框架中与java一样,同样可以加载静态资源,加载的资源可以用url进行访问

conn := gin.Default()
//加载静态文件
conn.Static("/images", "./images")
//用法与Static()类似,但是可以自定义http.FileSystem
conn.StaticFS("/static", http.Dir("./static"))
//加载html
conn.StaticFile("index", "index.html")
conn.Run()

三、泛绑定

泛绑定,泛,广泛,看下面的代码GET请求中的url:"/actives/:name/*active",这里的active参数前面是*,从这里开始,后面的请求可输入任意段url,也就是在请求的时候后面可以输入为:“/actives/:chen/jidji/fdffd/renyi”,后面可随意输入

conn:=gin.Default()
conn.GET("/actives/:name/*active", func(context *gin.Context) {
		name := context.Param("name")
		active := context.Param("active")
		context.JSON(http.StatusOK, gin.H{
			name:   name,
			active: active,
		})
	})
conn.Run()

四、路由分组

gin中的路由分组其实就是将相同的url段提取出来,使用grop()方法来拼接,减少代码的冗余

conn:=gin.Default()
c := conn.Group("/posts/")
{
		//括号也可不要,
		//这里照常写请求
}
conn.Run()

例子

//定义结构体,结构体设置了uri,表名参数名称以及参数必须传入
type Person struct {
	Name string `uri:"name"`
	Id   int    `uri:"id"`
}
func main(){
	conn:=gin.Default()
	//路由分组
	c := conn.Group("/posts/")
	{
		var p Person //定义结构体,结构体设置了uri参数,会自动传入
		//get请求,传递的参数都是从url上传输,结构体设置为uri,
		c.GET(":name/:id", func(context *gin.Context) {
			if err := context.ShouldBindUri(&p); err != nil {
				context.Status(404)
				return
			}
			context.JSON(http.StatusOK, gin.H{
				"name": p.Name,
				"id":   p.Id,
			})
		})
		//post请求,这里设置的相对路径为空,所以请求是直接访问上面Group()方法的路径
		c.POST("", func(context *gin.Context) {
				context.JSON(http.StatusOK, gin.H{
					"name": p.Name,
				})
		})
	}
	conn.Run()
}

五、参数获取

通过上面的几个例子,能明白一些参数的获取方法,上面的都是直接传输在相对路径上的属性值,下面展示在url中不显示属性应该如何传输,也就是在请求中只有相对路径,没有属性值的时候,一般也都是这样子传输的,调用请求,传输属性

1、GET请求获取参数
conn:=gin.Default()
//路由分组
c := conn.Group("/posts/")
{
	//在url上传参数的值与在key上传参数的值应用场景有何不同???
	//上面写的传输方式都是这样子的,直接在url上输入参数的值
	//c.GET("parameter/:firstName/:lastName", func(context *gin.Context) {
		//当参数的值不适合传入结构体,该当如何
		c.GET("parameter", func(context *gin.Context) {
			//获取值并且设置默认值
			firstName := context.DefaultQuery("firstName", "chen")
			//获取值,没有设置默认值
			lastName := context.Query("lastName")
			context.JSON(http.StatusOK, gin.H{
				"firstName": firstName,
				"lastName":  lastName,
			})
		})
}

在这里插入图片描述

2、GET请求获取map参数和Array参数
//这里省略gin实例和group方法,还有执行Run方法......
//queryArray、queryMap
//http://localhost:8080/posts/array?user=chen,12,男
c.GET("array", func(context *gin.Context) {
	user := context.QueryArray("user")
	context.JSON(http.StatusOK, gin.H{
		"user": user,
	})
})
//http://localhost:8080/posts/map?user[name]=chen&user[age]=15
c.GET("map", func(context *gin.Context) {
	user := context.QueryMap("user")
	context.JSON(http.StatusOK, gin.H{
		"user": user,
	})
})
3、POST获取参数

使用方法基本与GET请求一样


func postHandle(context *gin.Context) {
	name := context.PostForm("name")
	age := context.DefaultPostForm("age", "12")
	user := context.PostFormArray("user")
	userMap := context.PostFormMap("userMap")
	context.JSON(http.StatusOK, gin.H{
		"name":    name,
		"age":     age,
		"user":    user,
		"userMap": userMap,
	})
}
func postHandle2(context *gin.Context) {
	id := context.Param("id")
	username := context.PostForm("username")
	context.JSON(http.StatusOK, gin.H{
		"id":       id,
		"username": username,
	})
}
func main(){
	conn:=gin.Default()
	c:=conn.Group("/posts")
	{
		//post form表单请求,用法与Get一致
		c.POST("form_post", postHandle)
		//query+post form
		c.POST("query_post_form/:id", postHandle2)
	}
	conn.Run()
}

六、数据绑定

数据绑定其实就是将请求传的数据绑定到结构体参数中,主要是通过ShouldBindWith(obj,binding.Query)和MustBindWith(obj, binding.Query),他们的实现方法分别是BindQuery()ShouldBindQuery(),两种的使用没有什么区别,BindQuery在绑定数据出现错误时,状态码会显示为400

type User struct{
	ID string `form:"id" binding:"required,uuid"`
}
func mian(){
	conn := gin.Default()
	c := conn.Group("/posts")
	c.GET("user/", func(context *gin.Context) {
		var user User
		if err := context.BindQuery(&user); err != nil { //若存在绑定错误,状态码会显示为400
			//if err := context.ShouldBindQuery(&user); err != nil {
			context.JSON(404, gin.H{
				"code":    "404",
				"message": err.Error(),
			})
			return
		}
		context.JSON(200, gin.H{
			"code": "200",
			"ID":   user.ID,
		})
	})
	c.POST("/user_json", func(context *gin.Context) {
			var user User
			if err := context.ShouldBindJSON(&user); err != nil {
				context.JSON(404, gin.H{
					"code":    404,
					"message": "数据绑定错误",
				})
				return
			}
			context.JSON(200, gin.H{
				"code": 200,
				"id":   user.ID,
			})
		})
	conn.Run()
}

数据绑定的方法有很多,有通过json的,也有通过form表单的,调用方法的时候需要与实体类绑定的方法对应
在这里插入图片描述

七、中间件

gin框架允许开发者在处理请求的过程中,加入自己的钩子(hook)函数官方使用说明

1、内置中间件

在这里插入图片描述

2、自定义中间键

内置中间键中每个方法的使用情况都不一样,但是设置作用域全局路由、分组路由、单个路由都是一样的,而自定义中间键其实与定义方法类似,定义自己的方法,然后进行使用,下面是自定义中间键案例演示

公用代码


func loginAuth(context *gin.Context) {
	fmt.Println("我是登录保护中间键")
}
func loginHandles(context *gin.Context) {
	context.JSON(200, gin.H{
		"code":    200,
		"message": user,
	})
}

(1)全局使用(Gloabl)
func main() {
	conn := gin.Default()
	//use:全局使用中间件(Gloabl),下面的所有请求在执行之前都会调用到loginAuth方法
	conn.Use(loginAuth) 
	conn.POST("login", loginHandles)
	conn.GET("login", loginHandles)
	conn.Run()
}
(2)分组Group(Router Group)

在group下的请求在发送请求时,会先指定中间件,然后再执行请求内容

func main(){
	conn := gin.Default()
	c := conn.Group("/user/", loginAuth)  //Router Group
	{
		c.POST("login",loginHandles)
	}
	conn.Run()
}
(3)单个路由(SingleRouter)

在请求中添加自己自定义的中间件,在执行请求之前会先指定该中间件

func main() {
	conn := gin.Default()
	c := conn.Group("/user/")
	{
		c.POST("login", loginAuth, loginHandles) //SingleRouter
	}
	conn.Run()
}
(4)官方自定义中间件方式
//官方自定义中间件定义方式
func refererMiddleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		header := context.GetHeader("Referer")
		if header == "" {
			context.AbortWithStatusJSON(200, gin.H{
				"message": "非法访问",
			})
		}
		context.Next()
	}
}

func loginHandles(context *gin.Context) {
	context.JSON(200, gin.H{
		"message": "成功",
	})
}

func main(){
	//这里调用中间件是方法,与上面不同,上面是方法的名称
	conn.POST("login", refererMiddleware(), loginHandles)
}
3、Next函数

Next函数是与中间件一起使用,在中间件中使用Next函数,执行流程就会变为:执行中间件1 ->中间件1Next() ->执行中间件2 ->中间件2Next() -> 执行请求核心代码 ->原路返回执行中间件2Next()下代码 -> 原路返回执行中间件1Next()下代码 (洋葱模型

package main

import (
	"log"

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

func middleware1(context *gin.Context) {
	log.Println("middleware1 in...")
	context.Set("key", 1000)
	log.Println("middleware1 before next....")
	context.Next()
	log.Println("middleware1 after...")
	log.Println("middleware1 done...")
	context.JSON(200, gin.H{
		"message": context.GetInt("key"),
	})
}

func middleware2(context *gin.Context) {
	log.Println("middleware2 in...")
	log.Println("middleware2 before next....")
	context.Next()
	log.Println("middleware2 after...")
	log.Println("middleware2 done...")
}

func loginHandles(context *gin.Context) {
	log.Println("func in ...")
	key := context.GetInt("key")
	context.Set("key", key+2000)
}

func main() {
	conn := gin.Default()
	conn.POST("login", middleware1, middleware2, loginHandles) //SingleRouter
	conn.Run()
}

在这里插入图片描述

4、Abord函数

终止的意思,执行这个函数之后,不会在执行其他函数

package main

import (
	"log"

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

func middleware1(context *gin.Context) {
	log.Println("middleware1 in...")
	context.Set("key", 1000)
	log.Println("middleware1 before next....")
	if context.GetInt("key") == 1000 {
		context.Abort()		//终止
		return
	}
	//不会再进入第二个中间件以及请求核心了,但是这样子返回的json也都是空了
	//如何在使用Abort()的同时又有返回json ???
	//上面Abort()方法换为AbortWithStatusJson()
	//context.AbortWithStatusJSON(200, gin.H{
	//		"message": "请求失败",
	//})
	
	context.Next()			
	log.Println("middleware1 after...")		//如果不加return会执行这里
	log.Println("middleware1 done...")
}

func middleware2(context *gin.Context) {
	log.Println("middleware2 in...")
	log.Println("middleware2 before next....")
	context.Next()
	log.Println("middleware2 after...")
	log.Println("middleware2 done...")
}

func loginHandles(context *gin.Context) {
	log.Println("func in ...")
	key := context.GetInt("key")
	context.Set("key", key+2000)
	context.JSON(200, gin.H{
		"message": context.GetInt("key"),
	})
}

func main() {
	conn := gin.Default()
	conn.POST("login", middleware1, middleware2, loginHandles) //SingleRouter
	conn.Run()
}

八、golang databaseSql基本使用

Go语言中database/sql包提供了保证SQL或类SQL数据库的泛用接口,但不提供具体的数据库驱动,也就是在使用的时候,需要注入一个数据库驱动

1、连接数据库

连接数据库调用go自身自带的方法,但是必须引入外部数据库驱动,即便是不使用

package main
import (
	"database/sql"
	"fmt"

	_ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

func initializeDatabase() (err error) {
	dsn := "root:root@tcp(localhost:3306)/blog?charset=utf8mb4&parseTime=True&loc=Local"
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return
	}
	//连接数据库的真正方法
	if err = db.Ping(); err != nil {
		return
	}
	return nil
}
func main() {
	if err := initializeDatabase(); err != nil {
		panic(any(err))
	}
	fmt.Println("连接数据库成功")
}

2、增删改查

package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/go-sql-driver/mysql"
)

var db *sql.DB
//连接数据库
func initializeDatabase() (err error) {
	dsn := "root:root@tcp(localhost:3306)/blog?charset=utf8mb4&parseTime=True&loc=Local"
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return
	}
	if err = db.Ping(); err != nil {
		return
	}
	return nil
}

type user struct {
	Id       uint64
	Account  string
	Password string
}
type article struct {
	Id        uint32
	Title     string
	MainBody  string
	Time      string
	AccountId string
}

//查询多条
func queryMultiRow() []article {
	sqlStr := "select * from article"
	rows, err := db.Query(sqlStr)
	if err != nil {
		log.Println(err)
	}
	defer rows.Close()
	articles := make([]article, 0)
	for rows.Next() {
		var article article
		if err := rows.Scan(&article.Id, &article.Time, &article.AccountId, &article.MainBody, &article.Title); err != nil {
			log.Printf("scan failed err%v", err)
		}
		articles = append(articles, article)
	}
	return articles

}
//更新
func update() {
	sqlStr := "update article set title=? where id=?"
	exec, err := db.Exec(sqlStr, "China", 1)
	if err != nil {
		log.Println(err)
		return
	}
	affected, err := exec.RowsAffected()
	if err != nil {
		log.Println(err)
	}
	fmt.Println(affected)
}

//查询一条
func query() {
	sqlStr := "select * from article where id = ?"
	var article article
	if err := db.QueryRow(sqlStr, 1).Scan(&article.Id, &article.Time, &article.AccountId, &article.MainBody, &article.Title); err != nil {

		log.Printf("scan failed err%v", err)
	}
	fmt.Println(article)
}
//删除
func delete() {
	sqlStr := "delete from article where id=?"
	exec, err := db.Exec(sqlStr, 1)
	if err != nil {
		log.Println(err)
		return
	}
	affected, err := exec.RowsAffected()
	if err != nil {
		log.Println(err)
		return
	}
	fmt.Println(affected)
}
//增加

func insert() {
	sqlStr := "insert into article(id,time, account_id, main_body,title) values(1,'2020/12/3','1','golang','gin') "
	exec, err := db.Exec(sqlStr)
	if err != nil {
		log.Println("添加失败:%v", err)
		return
	}
	id, err := exec.LastInsertId()
	if err != nil {
		log.Println("获取数据行数失败:%v", err)
		return
	}
	fmt.Println(id)

}

func main() {
	if err := initializeDatabase(); err != nil {
		panic(any(err))
	}
	fmt.Println("连接数据库成功")
	query()
	articles := queryMultiRow()
	fmt.Println(articles)
	fmt.Println("---------")
	update()
	delete()
}

九、sqlx

sqlx使用的方法会比golang提供的databaseSql比较简单,但sqlx有些也是在golang上进行封装的

1、连接数据库
package main

import (
	"fmt"
	"log"

	//mysql驱动包,同样不管使用不使用,都需要导入
	_ "github.com/go-sql-driver/mysql"
	//sqlx的包
	"github.com/jmoiron/sqlx"
)

var db *sqlx.DB

func initializeDatabase() (err error) {
	dsn := "root:root@tcp(localhost:3306)/blog?charset=utf8mb4&parseTime=True&loc=Local"
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		log.Println(err)
	}
	//设置数据库最大打开连接数
	db.SetMaxOpenConns(10)
	//空闲连接池中的最大连接数
	db.SetMaxIdleConns(10)
	return nil
}
2、查询单条
func querySingleRow() {
	sqlStr := "select id,title,main_body,time,account_id from article where id = ?"
	var article article
	//这里与golang提供的数据库连接调用的方法不同,直接提供整个结构体的地址就可以,但是使用驼峰命名需要在属性后面声明
	if err := db.Get(&article, sqlStr, 1); err != nil {
		log.Printf("获取数据失败,%v", err)
		return
	}
	fmt.Printf("成功获取数据,%v", article)
}
3、查询多条
//查询多条
func queryMultiRows() []article {
	sqlStr := "select * from article"
	var articles []article
	if err := db.Select(&articles, sqlStr); err != nil {
		log.Printf("获取数据出错:%v", err)
	}
	return articles
}
4、更改数据
func update() {
	sqlStr := "update article set title = ? where id = ?"
	exec, err := db.Exec(sqlStr, "广州", 2)
	if err != nil {
		log.Println(err)
		return
	}
	affected, err := exec.RowsAffected()
	if err != nil {
		log.Print(err)
		return
	}
	fmt.Print("\n改变行数", affected)
}
5、插入数据
func insert() {
	sqlStr := "insert into article(id,title,main_body,time,account_id) values (?,?,?,?,?)"
	exec, err := db.Exec(sqlStr, 5, "golang", "真多", time.Now(), "1")
	if err != nil {
		log.Print(err)
		return
	}
	rows, err := exec.LastInsertId()
	if err != nil {
		log.Print(err)
	}
	fmt.Printf("id为:%v", rows)
}
6、删除数据
func delete() {
	sqlStr := "delete from article where id = ?"
	exec, err := db.Exec(sqlStr, 4)
	if err != nil {
		log.Println("删除出错:", err)
		return
	}
	affected, err := exec.RowsAffected()
	if err != nil {
		log.Println("删除出错:", err)
		return
	}
	fmt.Println(affected)
}
7、查询多条
func selectNamedQuery() {
	sqlStr := "select * from article where title = :title"
	query, err := db.NamedQuery(sqlStr, map[string]interface{}{
		"title": "golang",
	})
	if err != nil {
		log.Println(err)
	}
	defer query.Close()
	for query.Next() {
		var article article
		if err := query.StructScan(&article); err != nil {
			fmt.Printf("", err)
			continue
		}
		fmt.Printf("%v", article)
	}
}
8、插入多条
func batchInsert() {
	articles := []article{
		{6, "golang", "golang", "2022/3/02", "1"},
		{7, "golang", "golang", "2022/3/02", "1"},
	}
	sqlStr := "insert into article(id,title,main_body,time,account_id) values (:id,:title,:main_body,:time,:account_id)"
	_, err := db.NamedExec(sqlStr, articles)
	if err != nil {
		log.Println(err)
	}
	fmt.Println("success")
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值