这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
这篇文章介绍如何搭建一个go 语言的web项目,介绍如何读取配置文件,设置路由信息,实现一个简单的数据库查询
读取配置文件
导入工具
使用 viper工具
读取配置文件中的内容
导入viper
go get github.com/spf13/viper
读取配置文件
配置文件
在根目录下创建config.yml,作为配置文件
system:
# 设定模式(debug/release/test,正式版改为release)
mode: debug
# url前缀
url-path-prefix: /douyin
# 主机名
host: localhost
# 程序监听端口
port: 8080
mysql:
# 用户名
username: root
# 密码
password: xxx
# 数据库名
database: mini_douyin
# 主机地址
host: localhost
# 端口
port: 3306
# 连接字符串参数
query: parseTime=True&loc=Local&timeout=10000ms
# 是否打印日志
log-mode: true
# 数据库表前缀(无需再末尾添加下划线, 程序内部自动处理)
table-prefix: tb
# 编码方式
charset: utf8mb4
# 字符集(utf8mb4_general_ci速度比utf8mb4_unicode_ci快些)
collation: utf8mb4_general_ci
config.go
在项目中创建config.go
读取配置文件
总体思路:
- 创建结构体,对应yml 文件中的格式。示例代码中,创建一个结构体
config
,对应配置文件的第一层mysql
****、system
,然后再创建具体的结构体MysqlConfig
,System
,对应第一层下所有的键值对;
- 使用viper 读取 配置文件的信息。使用
viper
将配置文件的信息映射到结构体config
中,再将其读到全局配置变量Conf
中即可,然后就可以通过Conf来获取配置文件中的值;
package config
import (
"fmt"
"github.com/spf13/viper"
"os"
)
// Conf 全局配置变量
var Conf = new(config)
type config struct {
Mysql *MysqlConfig `mapstructure:"mysql" json:"mysql"`
System *System `mapstructure:"system" json:"system"`
}
func InitConfig() {
workDir, err := os.Getwd()
if err != nil {
panic(fmt.Errorf("读取应用目录失败:%s \n", err))
}
// 设置文件路径
viper.SetConfigName("config")
viper.SetConfigType("yml")
viper.AddConfigPath(workDir + "./")
err = viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("读取配置文件失败:%s \n", err))
}
// 将viper 中的json 信息映射到 全局Conf中
if err := viper.Unmarshal(Conf); err != nil {
panic(fmt.Errorf("初始化配置文件失败:%s \n", err))
}
}
// MysqlConfig 对应yml的中 mysql
type MysqlConfig struct {
Username string `mapstructure:"username" json:"username"`
Password string `mapstructure:"password" json:"password"`
Database string `mapstructure:"database" json:"database"`
Host string `mapstructure:"host" json:"host"`
Port int `mapstructure:"port" json:"port"`
Query string `mapstructure:"query" json:"query"`
LogMode bool `mapstructure:"log-mode" json:"logMode"`
TablePrefix string `mapstructure:"table-prefix" json:"tablePrefix"`
Charset string `mapstructure:"charset" json:"charset"`
Collation string `mapstructure:"collation" json:"collation"`
}
// System 对应yml中的system
type System struct {
Mode string `mapstructure:"mode" json:"mode"`
UrlPathPrefix string `mapstructure:"url-path-prefix" json:"urlPathPrefix"`
Host string `mapstructure:"host" json:"host"`
Port int `mapstructure:"port" json:"port"`
}
database.go
以上读取了文件中的信息,那么我们就可以通过全局的Conf
来配置我们数据库的信息了。
var DB *gorm.DB
func InitMysql() {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&collation=%s&%s",
config.Conf.Mysql.Username,
config.Conf.Mysql.Password,
config.Conf.Mysql.Host,
config.Conf.Mysql.Port,
config.Conf.Mysql.Database,
config.Conf.Mysql.Charset,
config.Conf.Mysql.Collation,
config.Conf.Mysql.Query,
)
db, err := gorm.Open("mysql", dsn)
if err != nil {
log.Printf("数据库连接错误:%s", err)
return
}
// 全局赋值
DB = db
log.Printf("数据库连接成功")
}
设置路由信息
导入gin
go get -u github.com/gin-gonic/gin
建立routes.go文件
创建 InitRoutes
函数,通过Conf
获取前置路径,建立路由分组
package routes
import (
"github.com/gin-gonic/gin"
"mini-douyin/config"
"mini-douyin/middleware"
)
func InitRoutes() *gin.Engine {
engine := gin.Default()
// 配置全局跨域中间件
engine.Use(middleware.CORSMiddleware())
// 路由分组 添加前缀
group := engine.Group(config.Conf.System.UrlPathPrefix)
InitUserRoutes(group)
return engine
}
建立user_route.go文件
设置详细的路由信息即可,建立InitUserRoutes
函数,然后在routes.go
中调用即可
package routes
import (
"github.com/gin-gonic/gin"
"mini-douyin/controller"
)
// InitUserRoutes 注册用户路由
func InitUserRoutes(r *gin.RouterGroup) gin.IRoutes {
userController := controller.NewUserController()
router := r.Group("/user")
{
router.GET("", userController.GetUserInfo)
}
return r
}
启动项目
编写main.go
文件,调用 InitRoutes
,InitConfig
, InitMysql
方法,然后使用gin启动服务即可。
package main
func main() {
// 加载配置文件到全局配置结构体
config.InitConfig()
// 初始化数据库
common.InitMysql()
// 注册所有路由
r := routes.InitRoutes()
host := config.Conf.System.Host
port := config.Conf.System.Port
srv := &http.Server{
Addr: fmt.Sprintf("%s:%d", host, port),
Handler: r,
}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Printf("listen: %s\n", err)
}
}()
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Printf("Shutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Printf("Server forced to shutdown: %s", err)
}
log.Printf("Server exiting!")
}
实现简单查询
建立controller
、service
、repository
文件夹
其实就是类似于Java的目录结构,有点类似依赖注入的实现,这里就不展开赘述了。附上各层的代码:
controller层代码
package controller
import (
"github.com/gin-gonic/gin"
"log"
"mini-douyin/model/request/user"
"mini-douyin/service"
"net/http"
)
type IUserController interface {
GetUserInfo(c *gin.Context) // 获取当前登录用户信息
}
type UserController struct {
UserService service.IUserService
}
// NewUserController 构造函数
func NewUserController() IUserController {
userService := service.NewUserService()
userController := UserController{UserService: userService}
return userController
}
func (uc UserController) GetUserInfo(c *gin.Context) {
var userInfo user.InfoRequest
err := c.ShouldBindQuery(&userInfo)
if err != nil {
log.Printf("参数错误: %v", err)
return
}
infoResponse, err := uc.UserService.GetUserById(userInfo)
if err != nil {
return
}
c.JSON(http.StatusOK, infoResponse)
}
service 层代码
package service
import (
"log"
req_user "mini-douyin/model/request/user"
resp_user "mini-douyin/model/response/user"
"mini-douyin/repository"
"strconv"
)
type IUserService interface {
GetUserById(request req_user.InfoRequest) (resp_user.InfoResponse, error)
}
type UserService struct {
UserRepository repository.IUserRepository
}
// NewUserService 构造函数
func NewUserService() IUserService {
userRepository := repository.NewUserRepository()
userService := UserService{UserRepository: userRepository}
return userService
}
func (u UserService) GetUserById(request req_user.InfoRequest) (resp_user.InfoResponse, error) {
id, i2 := strconv.Atoi(request.UserId)
infoResponse := resp_user.InfoResponse{}
if i2 != nil {
log.Printf("格式转化错误: %v", request)
return infoResponse, i2
}
userInfo, i2 := u.UserRepository.GetUserById(int64(id))
log.Printf("用户信息为:%v", userInfo)
infoResponse.User = userInfo
return infoResponse, nil
}
repository层代码
package repository
import (
"fmt"
"mini-douyin/common"
"mini-douyin/model/domain"
)
type IUserRepository interface {
GetUserById(id int64) (domain.User, error) // 获取单个用户
}
// UserRepository 定义一个结构体
type UserRepository struct {
}
// NewUserRepository UserRepository构造函数
func NewUserRepository() IUserRepository {
return UserRepository{}
}
// GetUserById 获取单个用户
func (ur UserRepository) GetUserById(id int64) (domain.User, error) {
fmt.Println("GetUserById---")
var user domain.User
err := common.DB.Where("user_id = ?", id).First(&user).Error
return user, err
}