四时宝库

程序员的知识宝库

2、Gin搭建Blog API's (一)——gin框架开发blog实例

思考

首先,在一个初始项目开始前,大家都要思考一下

  1. 各种的程序配置写在代码中,好吗
  2. API的错误码硬编在程序中,合适吗
  3. db句柄谁都去Open,好吗
  4. 获取分页等公共参数,不统一管理起来,好吗

显然在较正规的项目中,这些问题的答案都是不可以

为了解决这些问题,我们挑选一款读写配置文件的库,本系列中选用go-ini/ini ,它的中文文档https://ini.unknwon.io/。大家需要先简单阅读它的文档,再接着完成后面的内容。

我们还会编写一个简单的API错误码包,并且完成一个Demo示例和讲解知识点,便于后面的学习。

介绍和初始化项目

初始工作区

首先,我们需要增加一个工作区(GOPATH)路径用于我们的Blog项目。

将你新的工作区加入到/etc/profile中的GOPATH环境变量中, 并在新工作区中,建立bin、pkg、src三个目录。

在src目录下创建blog目录,初始的目录结构:

Bash
$GOPATH
├── bin
├── pkg
└── src
 └── blog

初始化项目目录

Bash
blog/
├── conf
├── middleware
├── models
├── pkg
├── routers
└── runtime
  • conf:用于存储配置文件
  • middleware:应用中间件
  • models:应用数据库模型
  • pkg:第三方包
  • routers 路由逻辑处理
  • runtime 应用运行时数据

初始项目数据库

新建blog数据库,编码为utf8_general_ci

在blog数据库下,新建以下表

1、 标签表

Bash
CREATE TABLE `blog_tag` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `name` varchar(100) DEFAULT '' COMMENT '标签名称',
 `created_on` int(10) unsigned DEFAULT '0' COMMENT '创建时间',
 `created_by` varchar(100) DEFAULT '' COMMENT '创建人',
 `modified_on` int(10) unsigned DEFAULT '0' COMMENT '修改时间',
 `modified_by` varchar(100) DEFAULT '' COMMENT '修改人',
 `deleted_on` int(10) unsigned DEFAULT '0',
 `state` tinyint(3) unsigned DEFAULT '1' COMMENT '状态 0为禁用、1为启用',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='文章标签管理';

2、 文章表

Bash
CREATE TABLE `blog_article` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `tag_id` int(10) unsigned DEFAULT '0' COMMENT '标签ID',
 `title` varchar(100) DEFAULT '' COMMENT '文章标题',
 `desc` varchar(255) DEFAULT '' COMMENT '简述',
 `content` text,
 `created_on` int(11) DEFAULT NULL,
 `created_by` varchar(100) DEFAULT '' COMMENT '创建人',
 `modified_on` int(10) unsigned DEFAULT '0' COMMENT '修改时间',
 `modified_by` varchar(255) DEFAULT '' COMMENT '修改人',
 `deleted_on` int(10) unsigned DEFAULT '0',
 `state` tinyint(3) unsigned DEFAULT '1' COMMENT '状态 0为禁用1为启用',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='文章管理';

3、 认证表

Bash
CREATE TABLE `blog_auth` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `username` varchar(50) DEFAULT '' COMMENT '账号',
 `password` varchar(50) DEFAULT '' COMMENT '密码',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `blog`.`blog_auth` (`id`, `username`, `password`) VALUES (null, 'test', 'test123456');

编写项目配置包

拉取go-ini/ini的依赖包

Bash
go get -u github.com/go-ini/ini

我们需要编写基础的应用配置文件,在blog的conf目录下新建app.ini文件,写入内容:

Bash
#debug or release
RUN_MODE = debug
[app]
PAGE_SIZE = 10
JWT_SECRET = 23347$040412
[server]
HTTP_PORT = 8000
READ_TIMEOUT = 60
WRITE_TIMEOUT = 60
[database]
TYPE = mysql
USER = 数据库账号
PASSWORD = 数据库密码
#127.0.0.1:3306
HOST = 数据库IP:数据库端口号
NAME = blog
TABLE_PREFIX = blog_

建立调用配置的setting模块,在blog的pkg目录下新建setting目录,新建setting.go文件,写入内容:

Bash
package setting
import (
	"log"
	"time"
	"github.com/go-ini/ini"
)
var (
	Cfg *ini.File
	RunMode string
	
	HTTPPort int
	ReadTimeout time.Duration
	WriteTimeout time.Duration
	PageSize int
	JwtSecret string
)
func init() {
	var err error
	Cfg, err = ini.Load("conf/app.ini")
	if err != nil {
		log.Fatalf("Fail to parse 'conf/app.ini': %v", err)
	}
	LoadBase()
	LoadServer()
	LoadApp()
}
func LoadBase() {
	RunMode = Cfg.Section("").Key("RUN_MODE").MustString("debug")
}
func LoadServer() {
	sec, err := Cfg.GetSection("server")
	if err != nil {
		log.Fatalf("Fail to get section 'server': %v", err)
	}
	RunMode = Cfg.Section("").Key("RUN_MODE").MustString("debug")
	HTTPPort = sec.Key("HTTP_PORT").MustInt(8000)
	ReadTimeout = time.Duration(sec.Key("READ_TIMEOUT").MustInt(60)) * time.Second
	WriteTimeout = time.Duration(sec.Key("WRITE_TIMEOUT").MustInt(60)) * time.Second	
}
func LoadApp() {
	sec, err := Cfg.GetSection("app")
	if err != nil {
		log.Fatalf("Fail to get section 'app': %v", err)
	}
	JwtSecret = sec.Key("JWT_SECRET").MustString("!@)*#)!@U#@*!@!)")
	PageSize = sec.Key("PAGE_SIZE").MustInt(10)
}

编写API错误码包

建立错误码的errcode模块,在blog的pkg目录下新建errcode目录,新建code.go和msg.go文件,写入内容:

1、 code.go:

Bash
package errcode
const (
	SUCCESS = 200
	ERROR = 500
	INVALID_PARAMS = 400
	ERROR_EXIST_TAG = 10001
	ERROR_NOT_EXIST_TAG = 10002
	ERROR_NOT_EXIST_ARTICLE = 10003
	ERROR_AUTH_CHECK_TOKEN_FAIL = 20001
	ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 20002
	ERROR_AUTH_TOKEN = 20003
	ERROR_AUTH = 20004
)

2、 msg.go:

Bash
package errcode
var MsgFlags = map[int]string {
	SUCCESS : "ok",
	ERROR : "fail",
	INVALID_PARAMS : "请求参数错误",
	ERROR_EXIST_TAG : "已存在该标签名称",
	ERROR_NOT_EXIST_TAG : "该标签不存在",
	ERROR_NOT_EXIST_ARTICLE : "该文章不存在",
	ERROR_AUTH_CHECK_TOKEN_FAIL : "Token鉴权失败",
	ERROR_AUTH_CHECK_TOKEN_TIMEOUT : "Token已超时",
	ERROR_AUTH_TOKEN : "Token生成失败",
	ERROR_AUTH : "Token错误",
}
func GetMsg(code int) string {
	msg, ok := MsgFlags[code]
	if ok {
		return msg
	}
	return MsgFlags[ERROR]
}

编写工具包

在blog的pkg目录下新建util目录,

拉取com的依赖包

Bash
go get -u github.com/Unknwon/com

编写分页页码的获取方法

在util目录下新建pagination.go,写入内容:

Bash
package util
import (
	"github.com/gin-gonic/gin"
	"github.com/Unknwon/com"
	"blog/pkg/setting"
)
func GetPage(c *gin.Context) int {
	result := 0
	page, _ := com.StrTo(c.Query("page")).Int()
 if page > 0 {
 result = (page - 1) * setting.PageSize
 }
 return result
}

编写models init

拉取gorm的依赖包

Bash
go get -u github.com/jinzhu/gorm

拉取mysql驱动的依赖包

Bash
go get -u github.com/go-sql-driver/mysql

完成后,在blog的models目录下新建models.go,用于models的初始化使用

Bash
package models
import (
	"log"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"blog/pkg/setting"
)
var db *gorm.DB
type Model struct {
	ID int `gorm:"primary_key" json:"id"`
	CreatedOn int `json:"created_on"`
	ModifiedOn int `json:"modified_on"`
}
func init() {
	var (
		err error
		dbType, dbName, user, password, host, tablePrefix string
	)
	sec, err := setting.Cfg.GetSection("database")
	if err != nil {
		log.Fatal(2, "Fail to get section 'database': %v", err)
	}
	dbType = sec.Key("TYPE").String()
	dbName = sec.Key("NAME").String()
	user = sec.Key("USER").String()
	password = sec.Key("PASSWORD").String()
	host = sec.Key("HOST").String()
	tablePrefix = sec.Key("TABLE_PREFIX").String()
	db, err = gorm.Open(dbType, fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local", 
		user, 
		password, 
		host, 
		dbName))
	if err != nil {
		log.Println(err)
	}
	gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string {
	 return tablePrefix + defaultTableName;
	}
	db.SingularTable(true)
	db.DB().SetMaxIdleConns(10)
	db.DB().SetMaxOpenConns(100)
}
func CloseDB() {
	defer db.Close()
}

编写项目启动、路由文件

最基础的准备工作完成啦,让我们开始编写Demo吧!

编写Demo

在gin-blog下建立main.go作为启动文件(也就是main包),

我们先写个Demo,帮助大家理解,写入文件内容:

Bash
package main
import (
 "fmt"
	"net/http"
 "github.com/gin-gonic/gin"
	"blog/pkg/setting"
)
func main() {
	router := gin.Default()
 router.GET("/test", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "test",
		})
	})
	s := &http.Server{
		Addr: fmt.Sprintf(":%d", setting.HTTPPort),
		Handler: router,
		ReadTimeout: setting.ReadTimeout,
		WriteTimeout: setting.WriteTimeout,
		MaxHeaderBytes: 1 << 20,
	}
	s.ListenAndServe()
}

执行go run main.go,查看命令行是否显示

Bash
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /test --> main.main.func1 (3 handlers)

在本机执行curl 127.0.0.1:8000/test,检查是否返回{"message":"test"}。

当前目录结构:

Bash
blog/
├── conf
│ └── app.ini
├── main.go
├── middleware
├── models
│ └── models.go
├── pkg
│ ├── errcode
│ │ ├── code.go
│ │ └── msg.go
│ ├── setting
│ │ └── setting.go
│ └── util
│ └── pagination.go
├── routers
│ └── router.go
├── runtime

重启服务,执行curl 127.0.0.1:8000/test查看是否正确返回。

下一节,我们将以我们的Demo为起点进行修改,开始编码!

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接