Gin 基础2(ORM、Redis)

11. 新增数据、封装 DB 初步、结合 Gin 实现查询 API;

在这里插入图片描述

SET FOREIGN_KEY_CHECKS=0;

DROP TABLE IF EXISTS `topics`;
CREATE TABLE `topics` (
  `topic_id` int(11) NOT NULL AUTO_INCREMENT,
  `topic_title` varchar(200) NOT NULL,
  `topic_short_title` varchar(50) DEFAULT NULL,
  `user_ip` varchar(20) NOT NULL,
  `topic_score` int(11) DEFAULT NULL,
  `topic_url` varchar(200) NOT NULL,
  `topic_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`topic_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4;

INSERT INTO `topics` VALUES ('8', 'TopicTitle', 'TopicShortTitle', '127.0.0.1', '0', 'testurl', '2019-03-07 22:01:25');

新增数据

  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/src/TopicModel.go
package src

import "time"

type Topics struct { //单个Topic实体
	TopicID int `json:"id" gorm:"PRIMARY_KEY"`
	TopicTitle string `json:"title" binding:"min=4,max=20"`
	TopicShortTitle string `json:"stitle" binding:"required,nefield=TopicTitle"`
	UserIp string `json:"ip" binding:"ipv4"`
	TopicScore int `json:"score" binding:"omitempty,gt=5"`
	TopicUrl string `json:"url" binding:"omitempty,topicurl"`
	TopicDate time.Time `json:"url" binding:"required"`
}
  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/main.go
package main

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
	. "topic.gin.test.com/src"
)

func main() {
	dsn := "root:asdf@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	// 新增数据
	topics := Topics{
		TopicTitle:"大标题",
		TopicShortTitle:"小标题",
		UserIp: "127.0.0.1",
		TopicScore: 2,
		TopicUrl: "xxx.com/?article=12",
		TopicDate: time.Now(),
	}
	fmt.Println(db.Create(&topics).RowsAffected)	// RowsAffected 判断是否插入成功
	fmt.Println(topics.TopicID)
}

封装 DB:测试 http://localhost:8080/v1/topics/9

  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/src/MyDB.go
package src

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var DBHelper *gorm.DB
var err error

func init() {
	dsn := "root:asdf@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
	// 此处等于
	DBHelper, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
	}

	//DBHelper.Debug()
}
  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/src/TopicModel.go
package src

import "time"

type Topics struct { //单个Topic实体
	TopicID int `json:"id" gorm:"PRIMARY_KEY"`
	TopicTitle string `json:"title" binding:"min=4,max=20"`
	TopicShortTitle string `json:"stitle" binding:"required,nefield=TopicTitle"`
	UserIp string `json:"ip" binding:"ipv4"`
	TopicScore int `json:"score" binding:"omitempty,gt=5"`
	TopicUrl string `json:"url" binding:"omitempty,topicurl"`
	TopicDate time.Time `json:"url" binding:"required"`

}
type TopicArray struct {
	TopicList []Topics `json:"topics" binding:"gt=0,lt=3,topics,dive"`
	TopicListSize int `json:"size"`
}

type TopicClass struct {
	ClassId int `gorm:"primaryKey"`	// 主键
	ClassName string
	ClassRemark string
	ClassType string `gorm:"Column:classtype"`	//指定字段名
}

// 单个 Topic 实体
type Topic struct {
	//TopicID int `json:"id"`		// 映射
	//TopicTitle string `json:"title" binding:"required"`	// require 必须传参
	TopicID int `json:"id"`
	TopicTitle string `json:"title" binding:"min=4,max=20"`	// 最小4,最大20字符
	TopicShortTitle string `json:"stitle" binding:"required,nefield=TopicTitle"`	// 短标题,nefield(not equal),和xx字段不能相等
	UserIP string `json:"ip" binding:"ipv4"`
	TopicScore int `json:"score" binding:"omitempty,gt=5"`	// 帖子积分,omitempty 可空,填了必须大于5
}

// 多条
//type Topics struct {
//	TopicList []Topic `json:"topics" binding:"gt=0,lt=3,dive"`	// 切片,dive进一步验证单条实体
//	TopicListSize int `json:"size"`
//}

// 创建实体
func CreateTopic(id int, title string) Topic {
	// return Topic{id, title}	// 只有两个字段,多字段建议写字段
	return Topic{TopicID:id, TopicTitle:title}
}

// 参数绑定
type TopicQuery struct {
	UserName string `json:"username" form:"username"`
	Page int `json:"page" form:"page" binding:"required"`	// binding 必须有
	PageSize int `json:"pagesize" form:"pagesize"`

}
  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/src/TopicDao.go
package src

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

// 登录判断,必须登录
func MustLogin() (gin.HandlerFunc) {
	return func(c *gin.Context) {
		if _, status := c.GetQuery("token"); !status {
			c.String(http.StatusUnauthorized, "缺少token参数")
			c.Abort()
		} else {
			c.Next()	// 不写也会往下走
		}

	}
}

// 有返回,调用 GetTopic 加 "()"
func GetTopic() (gin.HandlerFunc) {
	return func(c *gin.Context) {
		c.String(200,"获取topicid=%s的帖子",c.Param("topic_id"))
	}
}

// !处理数据库相关操作
func GetTopicDetail(c *gin.Context) {
	// c.String(200,"获取topicid=%s的帖子",c.Param("topic_id"))
	// c.JSON(200, CreateTopic(101, "帖子标题"))

	// 单独查询
	//dsn := "root:asdf@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
	//db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	//tid := c.Param("topic_id")
	//topics := Topics{}
	//db.Find(&topics, tid)
	//c.JSON(200, topics)

	tid := c.Param("topic_id")
	topics := Topics{}
	DBHelper.Find(&topics, tid)
	c.JSON(200, topics)
}

// 单帖新增
func NewTopic(c *gin.Context)  {
	//判断登录
	topic:=Topic{}
	err:=c.BindJSON(&topic)	// 寻找 json 参数
	if err!=nil{
		c.String(400,"参数错误:%s",err.Error())
	}else{
		c.JSON(200,topic)
	}
}

// 多帖批量新增
func NewTopics(c *gin.Context)  {
	//判断登录
	topics:=Topics{}
	err:=c.BindJSON(&topics)	// 寻找 json 参数
	if err!=nil{
		c.String(400,"参数错误:%s",err.Error())
	}else{
		c.JSON(200,topics)
	}
}

func DelTopic(c *gin.Context)  {
	//判断登录
	c.String(200,"删除帖子")
}

func GetTopicList(c *gin.Context) {
	query := TopicQuery{}
	err := c.BindQuery(&query)
	if err != nil{
		// 自定义错误信息
		c.String(400,"参数错误:%s",err.Error())
	}else{
		c.JSON(200,query)
	}

}

  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/main.go
package main

import (
	"github.com/gin-gonic/gin"
	. "topic.gin.test.com/src"
)


func main() {
	router:=gin.Default()

	v1:=router.Group("/v1/topics")	// 单条帖子
	{	// 代码块
		v1.GET("", GetTopicList)

		// 注意这里是不能加小括号的。否则变成执行 GetTopicDetail 函数了
		v1.GET("/:topic_id", GetTopicDetail)

		v1.Use(MustLogin())
		{
			v1.POST("",NewTopic)
			v1.DELETE("/:topic_id", DelTopic)
		}

	}

	// 多条帖子
	v2:=router.Group("/v1/mtopics")
	{
		v2.Use(MustLogin())
		{
			v2.POST("",NewTopics)
		}

	}
	router.Run()

}

12. 简单连接池设置、信号处理、优雅的退出程序;

查看数据库连接池

show PROCESSLIST

连接池基本参数

// 文档:https://gorm.io/zh_CN/docs/generic_interface.html

// db.DB() 可以返回 sql.db 对象
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/src/MyDB.go
package src

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

var DBHelper *gorm.DB
var err error

func init() {
	dsn := "root:asdf@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
	
	DBHelper, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
	}

	sqlDB, _ := DBHelper.DB()
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetMaxOpenConns(100)
	sqlDB.SetConnMaxLifetime(time.Hour)
}

代码演示,强行停止会有 Process finished with exit code 2

func main() {
	count:=0
	for {
		fmt.Println("执行",count)
		count++
		time.Sleep(time.Second*1)
	}
}

关于信号
在这里插入图片描述

  • 当我们按ctrl+c时,会发出SIGINT (这值是2),默认就是进程终止
  • 其他类似
  • SIGTERM :kill命令的默认信号 (信号值是15, 也就是通常的kill -15进程id)
  • SIGILL :kill -9
  • SIGQUIT :建立CORE文件终止进程,并且生成core文件
func main () {
	count:=0
		go func() {
			for {
				fmt.Println("执行",count)
				count++
				time.Sleep(time.Second*1)
			}
		}()
	
		c:=make(chan os.Signal)	// 创建信号chan
		go func() {
			// 创建一个 超时context,到期后会执行Done
			ctx,_:=context.WithTimeout(context.Background(),time.Second*5)
			select {
				case <-ctx.Done():
					// 重点,超时时间到了会发送SIGINT信号
					c<-os.Interrupt
			}
		}()
		// 监听信号
		signal.Notify(c)
		s:=<-c
		fmt.Println(s)
}

13. 数据库连接出错时关闭 web 服务:两种方式;

  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/src/MyInit.go
package src

import (
	"log"
	"os"
	"os/signal"
)


var ServerSigChan chan os.Signal

func init()  {
	ServerSigChan = make(chan os.Signal)
}
func ShutDownServer(err error)  {
	log.Println(err)
	ServerSigChan<-os.Interrupt
}

func ServerNotify()  {
	signal.Notify(ServerSigChan,os.Interrupt)
	<-ServerSigChan
}
  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/src/MyDB.go
package src

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

var DBHelper *gorm.DB
var err error

func InitDB() {
	dsn := "root:asdf@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
	// 此处等于
	DBHelper, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		ShutDownServer(err)
		return
		//log.Fatal("DB初始化错误:", err)
		//fmt.Println(err)
	}

	sqlDB, _ := DBHelper.DB()
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetMaxOpenConns(100)
	sqlDB.SetConnMaxLifetime(time.Hour)
}
package main

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"
	. "topic.gin.test.com/src"
)


func main() {
	router:=gin.Default()

	v1:=router.Group("/v1/topics")	// 单条帖子
	{	// 代码块
		v1.GET("", GetTopicList)

		// 注意这里是不能加小括号的。否则变成执行 GetTopicDetail 函数了
		v1.GET("/:topic_id", GetTopicDetail)

		v1.Use(MustLogin())
		{
			v1.POST("",NewTopic)
			v1.DELETE("/:topic_id", DelTopic)
		}

	}

	// 多条帖子
	v2:=router.Group("/v1/mtopics")
	{
		v2.Use(MustLogin())
		{
			v2.POST("",NewTopics)
		}

	}
	// router.Run()
	server := &http.Server{
		Addr:":8080",
		Handler:router,
	}
	go(func() { // 启动web服务
		err := server.ListenAndServe()
		if err!=nil{
			log.Fatal("服务器启动失败")
		}
	})()

	go(func() {
		InitDB()
	})()

	ServerNotify()
	//这里还可以做一些 释放连接或善后工作,暂时略
	ctx,cancel:=context.WithTimeout(context.Background(),time.Second*5)
	defer cancel()
	err:=server.Shutdown(ctx)
	if err!=nil{
		log.Fatalln("服务器关闭")
	}
	log.Println("服务器优雅退出")

}

14. redis 第三方库、连接池;

地址:https://github.com/gomodule/redigo
文档:https://godoc.org/github.com/gomodule/redigo/redis#pkg-examples
安装:go get github.com/gomodule/redigo/redis

  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/src/MyRedis.go
package src

import (
	"github.com/gomodule/redigo/redis"
	"time"
)

// 参考:https://pkg.go.dev/github.com/gomodule/redigo/redis#NewPool

var RedisDefaultPool *redis.Pool

func newPool(addr string) *redis.Pool {
	return &redis.Pool{
		MaxIdle: 3,
		IdleTimeout: 240 * time.Second,
		// Dial or DialContext must be set. When both are set, DialContext takes precedence over Dial.
		Dial: func () (redis.Conn, error) { return redis.Dial("tcp", addr) },
	}
}

func init()  {
	RedisDefaultPool=newPool("127.0.0.1:6379")
}
  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/main.go
package main

import (
	"context"
	"fmt"
	"github.com/gomodule/redigo/redis"
	"log"
	"os"
	"os/signal"
	"time"
	. "topic.gin.test.com/src"
)


func main() {
	// redis
	conn := RedisDefaultPool.Get()

	ret, err := redis.String(conn.Do("get","name"))	// 可执行 redis 原生命令
	if err != nil {
		log.Println(err)
		return
	}

	log.Println(ret)
}

15. 结合 gin 实现基本的 redis 缓存、缓存穿透简单处理;

之前我们实现一个 AP I是 GET /topic/8,现在最基本最简单的缓存是

  • 根据ID 查看数据库是否有值,如果有则取 redis的内容并返回、如果没有,则从数据库取 。

  • 取出来后 放入redis缓存,并设置过期时间

  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/src/TopicDao.go

package src

import (
	"github.com/gin-gonic/gin"
	"github.com/gomodule/redigo/redis"
	"github.com/pquerna/ffjson/ffjson"
	"log"
	"net/http"
)

// 登录判断,必须登录
func MustLogin() (gin.HandlerFunc) {
	return func(c *gin.Context) {
		if _, status := c.GetQuery("token"); !status {
			c.String(http.StatusUnauthorized, "缺少token参数")
			c.Abort()
		} else {
			c.Next()	// 不写也会往下走
		}

	}
}

// 有返回,调用 GetTopic 加 "()"
func GetTopic() (gin.HandlerFunc) {
	return func(c *gin.Context) {
		c.String(200,"获取topicid=%s的帖子",c.Param("topic_id"))
	}
}

// !处理数据库相关操作
func GetTopicDetail(c *gin.Context) {
	// c.String(200,"获取topicid=%s的帖子",c.Param("topic_id"))
	// c.JSON(200, CreateTopic(101, "帖子标题"))

	// 单独查询
	//dsn := "root:asdf@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
	//db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	//tid := c.Param("topic_id")
	//topics := Topics{}
	//db.Find(&topics, tid)
	//c.JSON(200, topics)

	//tid := c.Param("topic_id")
	//topics := Topics{}
	//DBHelper.Find(&topics, tid)
	//c.JSON(200, topics)

	// 缓存!
	tid := c.Param("topic_id")
	topics := Topics{}

	conn := RedisDefaultPool.Get()
	defer conn.Close()

	redisKey := "topic_"+tid

	ret, err := redis.Bytes(conn.Do("get", redisKey))
	if err != nil {	// 缓存无值
		DBHelper.Find(&topics, tid)
		retData, _ := ffjson.Marshal(topics)
		if topics.TopicID == 0 {  //代表从数据库没有匹配到
			conn.Do("setex",redisKey,20,retData)	// 防止缓存穿透,时间短一点
		} else { //正常数据 50秒缓存
			conn.Do("setex",redisKey,50,retData)
		}
		c.JSON(200,topics)
		log.Println("从数据库读取")
	} else { // 缓存有值
		log.Println("从 redis 读取")
		ffjson.Unmarshal(ret, &topics)
		c.JSON(200,topics)
	}

}

// 单帖新增
func NewTopic(c *gin.Context)  {
	//判断登录
	topic:=Topic{}
	err:=c.BindJSON(&topic)	// 寻找 json 参数
	if err!=nil{
		c.String(400,"参数错误:%s",err.Error())
	}else{
		c.JSON(200,topic)
	}
}

// 多帖批量新增
func NewTopics(c *gin.Context)  {
	//判断登录
	topics:=Topics{}
	err:=c.BindJSON(&topics)	// 寻找 json 参数
	if err!=nil{
		c.String(400,"参数错误:%s",err.Error())
	}else{
		c.JSON(200,topics)
	}
}

func DelTopic(c *gin.Context)  {
	//判断登录
	c.String(200,"删除帖子")
}

func GetTopicList(c *gin.Context) {
	query := TopicQuery{}
	err := c.BindQuery(&query)
	if err != nil{
		// 自定义错误信息
		c.String(400,"参数错误:%s",err.Error())
	}else{
		c.JSON(200,query)
	}

}

16. 使用 “装饰器模式” 实现 Redis 缓存的封装基本套路;

装饰器函数

// 其实就是一个高阶函数
func CacheDecorator(h gin.HandlerFunc) gin.HandlerFunc{
      return func(context *gin.Context) {

      }
}
// 追加参数使其更通通
func CacheDecorator(h gin.HandlerFunc,param string,redKeyPattern string,empty interface{})
// param是获取的参数ID,因为装饰器并不知道获取的ID参数是什么
// redKeyPattern 是redis中key的格式 ,因为装饰器也并不知道redis存的key是什么形式
// empty 传入一个空对象,用于转化

// 路由部分
 v1.GET("/:topic_id",CacheDecorator(GetTopicDetail))

// 目标函数修改
func GetTopicDetail(c *gin.Context)  {
	tid := c.Param("topic_id")
	topics := Topics{}
	DBHelper.Find(&topics,tid)
	c.Set("dbResult",topics)  // 关键部分
}
  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/src/Decorator.go
package src

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gomodule/redigo/redis"
	"github.com/pquerna/ffjson/ffjson"
	"log"
)

// 缓存装饰器
func CacheDecorator(h gin.HandlerFunc, param string, redKeyPattern string, empty interface{}) gin.HandlerFunc {
	return func(context *gin.Context) {
		//redis判断
		getID := context.Param(param) //得到ID值
		redisKey := fmt.Sprintf(redKeyPattern, getID)
		conn := RedisDefaultPool.Get()
		defer conn.Close()
		ret, err := redis.Bytes(conn.Do("get", redisKey))
		if err!=nil { // 缓存里没有
			h(context)	// 执行目标方法
			dbResult, exists := context.Get("dbResult")
			if !exists{
				dbResult = empty
			}
			retData, _ := ffjson.Marshal(dbResult)
			conn.Do("setex",redisKey,20,retData)
			context.JSON(200,dbResult)
			log.Println("从数据库读取")

		} else {//缓存有 ,直接抛出
			log.Println("从 redis读取")
			ffjson.Unmarshal(ret,&empty)
			context.JSON(200,empty)

		}

	}
}
  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/src/TopicDao.go
package src

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

// 登录判断,必须登录
func MustLogin() (gin.HandlerFunc) {
	return func(c *gin.Context) {
		if _, status := c.GetQuery("token"); !status {
			c.String(http.StatusUnauthorized, "缺少token参数")
			c.Abort()
		} else {
			c.Next()	// 不写也会往下走
		}

	}
}

// 有返回,调用 GetTopic 加 "()"
func GetTopic() (gin.HandlerFunc) {
	return func(c *gin.Context) {
		c.String(200,"获取topicid=%s的帖子",c.Param("topic_id"))
	}
}

// !!处理数据库相关操作
func GetTopicDetail(c *gin.Context) {
	// c.String(200,"获取topicid=%s的帖子",c.Param("topic_id"))
	// c.JSON(200, CreateTopic(101, "帖子标题"))

	// 单独查询
	//dsn := "root:asdf@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
	//db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	//tid := c.Param("topic_id")
	//topics := Topics{}
	//db.Find(&topics, tid)
	//c.JSON(200, topics)

	//tid := c.Param("topic_id")
	//topics := Topics{}
	//DBHelper.Find(&topics, tid)
	//c.JSON(200, topics)

	// redis
	//tid := c.Param("topic_id")
	//topics := Topics{}
	//
	//conn := RedisDefaultPool.Get()
	//defer conn.Close()
	//
	//redisKey := "topic_"+tid
	//
	//ret, err := redis.Bytes(conn.Do("get", redisKey))
	//if err != nil {	// 缓存无值
	//	DBHelper.Find(&topics, tid)
	//	retData, _ := ffjson.Marshal(topics)
	//	if topics.TopicID == 0 {  //代表从数据库没有匹配到
	//		conn.Do("setex",redisKey,20,retData)	// 防止缓存穿透,时间短一点
	//	} else { //正常数据 50秒缓存
	//		conn.Do("setex",redisKey,50,retData)
	//	}
	//	c.JSON(200,topics)
	//	log.Println("从数据库读取")
	//} else { // 缓存有值
	//	log.Println("从 redis 读取")
	//	ffjson.Unmarshal(ret, &topics)
	//	c.JSON(200,topics)
	//}

	tid:=c.Param("topic_id")
	topics:=Topics{}
	DBHelper.Find(&topics,tid)//从数据库取
	c.Set("dbResult",topics)

}

// 单帖新增
func NewTopic(c *gin.Context)  {
	//判断登录
	topic:=Topic{}
	err:=c.BindJSON(&topic)	// 寻找 json 参数
	if err!=nil{
		c.String(400,"参数错误:%s",err.Error())
	}else{
		c.JSON(200,topic)
	}
}

// 多帖批量新增
func NewTopics(c *gin.Context)  {
	//判断登录
	topics:=Topics{}
	err:=c.BindJSON(&topics)	// 寻找 json 参数
	if err!=nil{
		c.String(400,"参数错误:%s",err.Error())
	}else{
		c.JSON(200,topics)
	}
}

func DelTopic(c *gin.Context)  {
	//判断登录
	c.String(200,"删除帖子")
}

func GetTopicList(c *gin.Context) {
	query := TopicQuery{}
	err := c.BindQuery(&query)
	if err != nil{
		// 自定义错误信息
		c.String(400,"参数错误:%s",err.Error())
	}else{
		c.JSON(200,query)
	}

}
  • 文件 /Users/hualaoshuan/go/go-gin/api.gin.test.com/topic/main.go
package main

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"
	 . "topic.gin.test.com/src"
)

func main() {
	router:=gin.Default()

	v1:=router.Group("/v1/topics")	// 单条帖子
	{	// 代码块
		v1.GET("", GetTopicList)

		// 注意这里是不能加小括号的。否则变成执行 GetTopicDetail 函数了
		// v1.GET("/:topic_id", GetTopicDetail)
		// 调用装饰器
		v1.GET("/:topic_id",CacheDecorator(GetTopicDetail, "topic_id", "topic_%s", Topics{}))

		v1.Use(MustLogin())
		{
			v1.POST("",NewTopic)
			v1.DELETE("/:topic_id", DelTopic)
		}

	}

	// 多条帖子
	v2:=router.Group("/v1/mtopics")
	{
		v2.Use(MustLogin())
		{
			v2.POST("",NewTopics)
		}

	}
	// router.Run()

	server := &http.Server{
		Addr:":8080",
		Handler:router,
	}
	go(func() { // 启动web服务
		err := server.ListenAndServe()
		if err!=nil{
			log.Fatal("服务器启动失败")
		}
	})()

	go(func() {
		InitDB()
	})()

	ServerNotify()
	//这里还可以做一些 释放连接或善后工作,暂时略
	ctx,cancel:=context.WithTimeout(context.Background(),time.Second*5)
	defer  cancel()
	err:=server.Shutdown(ctx)
	if err!=nil{
		log.Fatalln("服务器关闭")
	}
	log.Println("服务器优雅退出")

}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值