Go Web项目学习之项目结构

news2024/11/27 14:46:47

风离不摆烂学习日志 Day4 — Go Web项目学习之项目结构

创建项目配置代理 下载加速

go 包代理 GOPROXY=https://goproxy.cn,direct

本项目学习自:

[github.com](https://github.com/gnimli/go-web-mini)

项目结构分层

├─common # casbin mysql zap validator 等公共资源
├─config # viper读取配置
├─controller # controller层,响应路由请求的方法
├─dto # 返回给前端的数据结构
├─middleware # 中间件
├─model # 结构体模型
├─repository # 数据库操作
├─response # 常用返回封装,如Success、Fail
├─routes # 所有路由
├─util # 工具方法
└─vo # 接收前端请求的数据结构

项目分析

main.go

package main

import (
   "context"
   "fmt"
   "go-web-mini/common"
   "go-web-mini/config"
   "go-web-mini/middleware"
   "go-web-mini/repository"
   "go-web-mini/routes"
   "net/http"
   "os"
   "os/signal"
   "syscall"
   "time"
)

func main() {

   // 加载配置文件到全局配置结构体
   config.InitConfig()

   // 初始化日志
   common.InitLogger()

   // 初始化数据库(mysql)
   common.InitMysql()

   // 初始化casbin策略管理器
   common.InitCasbinEnforcer()

   // 初始化Validator数据校验
   common.InitValidate()

   // 初始化mysql数据
   common.InitData()

   // 操作日志中间件处理日志时没有将日志发送到rabbitmq或者kafka中, 而是发送到了channel中
   // 这里开启3个goroutine处理channel将日志记录到数据库
   logRepository := repository.NewOperationLogRepository()
   for i := 0; i < 3; i++ {
      go logRepository.SaveOperationLogChannel(middleware.OperationLogChan)
   }

   // 注册所有路由
   r := routes.InitRoutes()

   host := "localhost"
   port := config.Conf.System.Port

   srv := &http.Server{
      Addr:    fmt.Sprintf("%s:%d", host, port),
      Handler: r,
   }

   // Initializing the server in a goroutine so that
   // it won't block the graceful shutdown handling below
   go func() {
      if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
         common.Log.Fatalf("listen: %s\n", err)
      }
   }()

   common.Log.Info(fmt.Sprintf("Server is running at %s:%d/%s", host, port, config.Conf.System.UrlPathPrefix))

   // Wait for interrupt signal to gracefully shutdown the server with
   // a timeout of 5 seconds.
   quit := make(chan os.Signal)
   // kill (no param) default send syscall.SIGTERM
   // kill -2 is syscall.SIGINT
   // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
   signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
   <-quit
   common.Log.Info("Shutting down server...")

   // The context is used to inform the server it has 5 seconds to finish
   // the request it is currently handling
   ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   defer cancel()
   if err := srv.Shutdown(ctx); err != nil {
      common.Log.Fatal("Server forced to shutdown:", err)
   }

   common.Log.Info("Server exiting!")

}

main.go

config.InitConfig()

首先我尝试 在config里使用 log包下的日志 打印 报这个错误

package go-web-mini
imports go-web-mini/common
imports go-web-mini/config
imports go-web-mini/common: import cycle not allowed

即不能循环依赖

关于项目中用到的这个包

"github.com/spf13/viper"

viper库
viper 是一个配置解决方案,拥有丰富的特性:

支持 JSON/TOML/YAML/HCL/envfile/Java properties 等多种格式的配置文件;
可以设置监听配置文件的修改,修改时自动加载新的配置;
从环境变量、命令行选项和io.Reader中读取配置;
从远程配置系统中读取和监听修改,如 etcd/Consul;
代码逻辑中显示设置键值。
————————————————
版权声明:本文为CSDN博主「小象裤衩」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_52000204/article/details/123450735

敲黑板 支持热更新

InitConfig 读取根目录下的配置文件 config.yml

// 设置读取配置信息
func InitConfig() {
	workDir, err := os.Getwd() // os.Getwd(): 为动态路径,你终端cd到哪里,它就取当前的dir(等价于./),用于做小工具

	if err != nil {
		panic(fmt.Errorf("读取应用目录失败:%s \n", err))
	}

	viper.SetConfigName("config")
	viper.SetConfigType("yml")
	viper.AddConfigPath(workDir + "./")

	//common.Log.Info("当前读取配置信息路径为", workDir)  //会有循环依赖错误 日志 依赖这个包
	println("当前读取配置信息路径为", workDir)

	// 读取配置信息
	err = viper.ReadInConfig()

	// 热更新配置
	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		// 将读取的配置信息保存至全局变量Conf
		if err := viper.Unmarshal(Conf); err != nil {
			panic(fmt.Errorf("初始化配置文件失败:%s \n", err))
		}
		// 读取rsa key
		Conf.System.RSAPublicBytes = util.RSAReadKeyFromFile(Conf.System.RSAPublicKey)
		Conf.System.RSAPrivateBytes = util.RSAReadKeyFromFile(Conf.System.RSAPrivateKey)
	})

	if err != nil {
		panic(fmt.Errorf("读取配置文件失败:%s \n", err))
	}
	// 将读取的配置信息保存至全局变量Conf
	if err := viper.Unmarshal(Conf); err != nil {
		panic(fmt.Errorf("初始化配置文件失败:%s \n", err))
	}
	// 读取rsa key
	Conf.System.RSAPublicBytes = util.RSAReadKeyFromFile(Conf.System.RSAPublicKey)
	Conf.System.RSAPrivateBytes = util.RSAReadKeyFromFile(Conf.System.RSAPrivateKey)

}

common.InitLogger()

配置日志输出位置和加载日志插件 Zap

Zap是非常快的、结构化的,分日志级别的Go日志库。

common.InitMysql()

从配置文件中读取 Mysql配置并把表 关联到相应的结构体上

database.go

package common

import (
	"fmt"
	"go-web-mini/config"
	"go-web-mini/model"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// 全局mysql数据库变量
var DB *gorm.DB

// 初始化mysql数据库
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,
	)
	// 隐藏密码
	showDsn := fmt.Sprintf(
		"%s:******@tcp(%s:%d)/%s?charset=%s&collation=%s&%s",
		config.Conf.Mysql.Username,
		config.Conf.Mysql.Host,
		config.Conf.Mysql.Port,
		config.Conf.Mysql.Database,
		config.Conf.Mysql.Charset,
		config.Conf.Mysql.Collation,
		config.Conf.Mysql.Query,
	)
	//Log.Info("数据库连接DSN: ", showDsn)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		// 禁用外键(指定外键时不会在mysql创建真实的外键约束)
		DisableForeignKeyConstraintWhenMigrating: true,
		 指定表前缀
		//NamingStrategy: schema.NamingStrategy{
		//	TablePrefix: config.Conf.Mysql.TablePrefix + "_",
		//},
	})
	if err != nil {
		Log.Panicf("初始化mysql数据库异常: %v", err)
		panic(fmt.Errorf("初始化mysql数据库异常: %v", err))
	}

	// 开启mysql日志
	if config.Conf.Mysql.LogMode {
		db.Debug()
	}
	// 全局DB赋值
	DB = db
	// 自动迁移表结构
	dbAutoMigrate()
	Log.Infof("初始化mysql数据库完成! dsn: %s", showDsn)
}

// 自动迁移表结构
func dbAutoMigrate() {
	DB.AutoMigrate(
		&model.User{},
		&model.Role{},
		&model.Menu{},
		&model.Api{},
		&model.OperationLog{},
	)
}

common.InitCasbinEnforcer()

1、Casbin 基本介绍

Casbin是一个强大的、高效的开源访问控制框架,网上的说明一大堆,我就不抄了,简单来说,以RABC举例,就是设立控制模型后。在需要判断用户有没有权限能访问的地方,使用Enforce()这个函数就会返回用户能否访问,就这么简单。

2、为什么要使用Casbin

如果没有这个框架,那么你需要一大堆的关联数据库查询才能知道这个用户能否访问,这个在gin的中间件时是不好的方法。所以,我们使用casbin,在前后端分离中,前端每次只要传一个包含用户的JWT,后端就知道当前访问的API是否有权限。另外,Casbin支持多语言,这样在策略不用改变的情况下,别的语言也可以使用。

common.InitValidate()

gin自定义数据校验器:github.com/go-playground/validator/v10 类似于java 的 @Valid 做数据校验的

image-20221122173906131

common.InitData()

初始化 mysql 数据 如果没有表结构 则生成表结构 有则 return 具体逻辑有待研究

image-20221122174533908

多线程执行储存操作日志

// 操作日志中间件处理日志时没有将日志发送到rabbitmq或者kafka中, 而是发送到了channel中
// 这里开启3个goroutine处理channel将日志记录到数据库
logRepository := repository.NewOperationLogRepository()
for i := 0; i < 3; i++ {
   go logRepository.SaveOperationLogChannel(middleware.OperationLogChan)
}

routes.InitRoutes()

初始化所有路由

package routes

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"go-web-mini/common"
	"go-web-mini/config"
	"go-web-mini/middleware"
	"time"
)

// 初始化
func InitRoutes() *gin.Engine {
	//设置模式
	gin.SetMode(config.Conf.System.Mode)

	// 创建带有默认中间件的路由:
	// 日志与恢复中间件
	r := gin.Default()
	// 创建不带中间件的路由:
	// r := gin.New()
	// r.Use(gin.Recovery())

	// 启用限流中间件
	// 默认每50毫秒填充一个令牌,最多填充200个
	fillInterval := time.Duration(config.Conf.RateLimit.FillInterval)
	capacity := config.Conf.RateLimit.Capacity
	r.Use(middleware.RateLimitMiddleware(time.Millisecond*fillInterval, capacity))

	// 启用全局跨域中间件
	r.Use(middleware.CORSMiddleware())

	// 启用操作日志中间件
	r.Use(middleware.OperationLogMiddleware())

	// 初始化JWT认证中间件
	authMiddleware, err := middleware.InitAuth()
	if err != nil {
		common.Log.Panicf("初始化JWT中间件失败:%v", err)
		panic(fmt.Sprintf("初始化JWT中间件失败:%v", err))
	}

	// 路由分组
	apiGroup := r.Group("/" + config.Conf.System.UrlPathPrefix)

	// 注册路由
	InitBaseRoutes(apiGroup, authMiddleware)         // 注册基础路由, 不需要jwt认证中间件,不需要casbin中间件
	InitUserRoutes(apiGroup, authMiddleware)         // 注册用户路由, jwt认证中间件,casbin鉴权中间件
	InitRoleRoutes(apiGroup, authMiddleware)         // 注册角色路由, jwt认证中间件,casbin鉴权中间件
	InitMenuRoutes(apiGroup, authMiddleware)         // 注册菜单路由, jwt认证中间件,casbin鉴权中间件
	InitApiRoutes(apiGroup, authMiddleware)          // 注册接口路由, jwt认证中间件,casbin鉴权中间件
	InitOperationLogRoutes(apiGroup, authMiddleware) // 注册操作日志路由, jwt认证中间件,casbin鉴权中间件

	common.Log.Info("初始化路由完成!")
	return r
}

main.go总结

  1. 初始化读取配置文件 config.yml
  2. 初始化日志记录
  3. 初始化Mysql连接操作
  4. 初始化权限控制插件
  5. 初始化字段校验插件
  6. 如果没有导入数据初始化数据 生成表结构和数据
  7. 启动3个goroutine 来记录操作日志
  8. 初始化所有路由和中间件(日志 跨域 JWT…)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/27094.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web概述18

JSP JSP是Sun为了解决动态生成HTML文档的技术&#xff0c;通过Servlet输出简单html页面信息都非常不方便。如果要输出一个复杂页面的时候&#xff0c;就更加的困难&#xff0c;而且不利于页面的维护和调试。所以sun公司推出一种叫做jsp的动态页面技术来实现对页面的输出繁锁工…

【设计模式】 - 结构型模式 - 适配器模式

目录标题1. 前言适配器模式1. 概述2. 结构3. 实现3.1 类适配器模式&#xff08; 继承&#xff09;--耦合度高代码实现3.2 对象适配器模式&#xff08;聚合&#xff09;代码实现类适配器与对象适配器的区别4. 适配器模式的优缺点1. 前言 结构型模式描述如何将类或对象按某种布局…

Borland Delphi 2005对Delphi语言的支持

Borland Delphi 2005对Delphi语言的支持 Borland Delphi是Borland Delphi的最新版本&#xff0c;它为用户的Microsoft Windows操作系统和1.1本身的Microsoft.NET Framework版本提供了快速应用程序开发。Delphi Borland 2005被认为具有主要的三个不同的字符&#xff0c;如Win32…

目标检测论文解读复现之十五:基于YOLOv5的光学遥感图像舰船 目标检测算法

前言 此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读最新目标检测算法论文&#xff0…

关于商业智能BI,今天只谈这五点

数据在当下的价值不断提高&#xff0c;但数据本身只是一种资产&#xff0c;一旦超过一定数据量就很难被人类理解&#xff0c;所以想要利用数据&#xff0c;就必须将数据转化为信息和知识&#xff0c;让管理者看到的是信息&#xff0c;而不是数据堆砌。 之前的文章里写过很多关…

打了10次电话,才总结出来的抖音封号原因分析,能避免大量封号

真正做过视频的人,应该是经常会面对封号 ,如果你号都没有封过,说明你还没有真正开始过 。这个话不是我讲的&#xff0c;是和一个日入3万的大V聊天&#xff0c;他说给我听的。你觉得这话对么&#xff1f;无论任何平台 ,都不会无缘无故封你号 ,平台需要大量的作者去生成内容 ,在符…

马来酰亚胺聚谷氨酸天冬氨酸聚合物药物载顺铂/mPEg-PGA纳米微球的制备

小编在此整理了马来酰亚胺聚谷氨酸天冬氨酸聚合物药物载顺铂/mPEg-PGA纳米微球的制备步骤&#xff0c;与小编一同来看&#xff01; 载顺铂mPEg-PGA纳米微球的制备&#xff1a; 采用生物相容性好,生物可降解的mPEG-PGA聚合物作为药物载体,通过膜透析法制备了负载顺铂纳米微球,以…

CAN总线在OSI模型中层级

ISO14229-1仅规定了应用层的实现&#xff0c;诊断可以基于不同的总线去实施&#xff0c;以基于CAN的UDS最为广泛。本文开始将陆续介绍CAN总线协议的规范与开发。 关联文章&#xff1a;UDS的OSI模型 文章目录OSI模型与CAN的关系在各层中CAN定义事项CAN总线网络ISO11898 和 ISO…

如何保证优秀的医疗器械设计?

如何保证优秀的产品设计?医疗器械设计领域的出现不是一蹴而就的&#xff0c;而是经历了时间的沉淀。 医疗设备的设计责任重大。这些产品有机会改变和拯救生命。它们可以在市场上销售几年或更长时间&#xff0c;这将影响人们管理自己或他人健康的方式。所以&#xff0c;医疗产品…

报错与解决 | 应用程序无法启动0x7b mysql

文章目录报错解决办法成功解决问题报错 根据“MySQL下载安装使用-完整详细步骤”下载安装好MySQL后&#xff0c;以管理员身份启动cmd&#xff0c;输入&#xff1a; "D:\mysql\mysql-5.7.31-winx64\bin \mysqld.exe" --initialize-insecure # 自己的mysql安装目录 …

C语言easyx颜色模块+案例

c语言exsyx学习颜色模型rgbhsvhsv转换为rgb案例 绘制彩虹窗体案例 绘制天空和彩虹画面颜色模型 1.1 什么是RGB颜色模型 不知道你有没有近距离看过各种电子显示屏。若非常近距离的观察电子显示屏&#xff0c;可以发现屏幕居然是由 一个一个的红色、绿色、蓝色的小点组成。 红…

毕业设计 - SSM中药店商城系统(含源码+论文)

文章目录1 项目简介2 实现效果2.1 界面展示3 设计方案3.1 概述3.2 系统流程3.3 系统结构设计4 项目获取1 项目简介 Hi&#xff0c;各位同学好呀&#xff0c;这里是M学姐&#xff01; 今天向大家分享一个今年(2022)最新完成的毕业设计项目作品&#xff0c;【基于SSM的中药店商…

半年销售100万辆 关注比亚迪后300万时代

11月16日&#xff0c;比亚迪第300万辆新能源汽车下线发布在比亚迪全球总部举行&#xff0c;标志着比亚迪成为首个达成这一里程碑的中国品牌。从“第1辆新能源汽车到第100万辆新能源汽车”用时13年、从“100万到200万”用时1年&#xff0c;从“200万到300万”仅用时半年&#xf…

HTML5——周技能检测——菜单编辑——2022年11月22日(考完)

HTML5——周技能检测——菜单编辑——2022年11月22日&#xff08;考完&#xff09; 一、语言和环境 1. 实现语言&#xff1a;HTML5。 2. 开发环境&#xff1a;VScode。 二、要求 1、完成下列菜单显示效果。 2、添加【:hover】选择器&#xff0c;鼠标悬停在文字上方时文字…

全面详解云计算

【导读】本文从企业IT建设发展痛点解析入手,从云计算的标准定义、云平台分类、云计算优劣势分析,到企业规模、业务类型与云平台适用以及企业上云流程简析,全面详解云计算,结合作者的思考和观点,为读者认识云计算提供一个全景式的概貌。 前言 大数据、人工智能、云计算可…

C++ 手动实现双线链表(作业版)

双向链表&#xff0c;并实现增删查改等功能 首先定义节点类&#xff0c;类成员包含当前节点的值&#xff0c; 指向下一个节点的指针和指向上一个节点的指针 //节点定义 template <typename T> class Node { public:Node<T>* prior;T value;Node<T>* next;N…

Python对Excel的读取

在python自动化中&#xff0c;经常会遇到对数据文件的操作&#xff0c;比如添加多名员工&#xff0c;但是直接将员工数据写在python文件中&#xff0c;不但工作量大&#xff0c;要是以后再次遇到类似批量数据操作还会写在python文件中吗&#xff1f; 应对这一问题&#xff0c;可…

【OpenDDS开发指南V3.20】第七章:运行时配置

配置方式 OpenDDS 包括一个基于文件的配置框架,用于配置全局选项以及与特定发布者和订阅者相关的选项,例如发现和传输配置。 OpenDDS 还允许通过命令行对有限数量的选项和配置 API 进行配置。 本章总结了 OpenDDS 支持的配置选项。 OpenDDS 配置涉及三个主要领域: 通用配…

QT 实现简单的天气 哈哈

前 言 前段时间开始接触QT&#xff0c;想着没事学习一下QT&#xff0c;这个小列子主要是用到了QJSON 和QNetworkAccessManager 来实现&#xff0c;QJSON 主要是用来json格式文件的解析&#xff0c;QNetworkAccessManager 主要来实现网络请求。 界面展示 界面上看非常low&…

解决java: 程序包org.springframework.boot不存在的解决方法

问题描述 最近IDEA抽风了&#xff0c;不管是新建SpringBoot工程&#xff0c;还是导入项目。 IDEA代码里面都会飘红~ Build项目时&#xff0c;会提示错误&#xff1a;错误:(3, 32) java: 程序包org.springframework.boot不存在 后来找到一个靠谱的解决方案&#xff0c;建议使…