「项目阅读系列」go-gin-example star 6.5k!(1)
文章目录
- 准备工作
- 适宜人群
- 项目信息
- 项目结构
- 代码阅读
- 主要模块代码
- 主函数模块
- router 路由模块
- auth 授权模块
- 数据库
- 修改文章请求分析
- 其他依赖
- 总结
准备工作
适宜人群
初学 go 语法,希望了解 go 项目的构建过程和方式。
项目信息
go-gin-example 项目是使用 gin 框架构建一个简易的 blog 服务,包括对 blog 的增删改查操作以及 blog tag 的增删改查等。
- 代码仓库:https://github.com/eddycjy/go-gin-example
- 版本:565e1a9395471e829abdb2201e00321c327626cd 第一次提交版本
项目结构
项目代码结构如下
- conf 配置相关
- middleware 中间件
- models 数据库相关对象以及操作
- pkg 项目相关的模块包
- routers 路由相关
- main 主函数

代码阅读
主要模块代码
首先看一下整体项目中比较重要的模块,包括 主函数、路由模块、授权模块、数据库模块。
主函数模块
func main() {router := routers.InitRouter()s := &http.Server{Addr: fmt.Sprintf(":%d", setting.HTTPPort),Handler: router,ReadTimeout: setting.ReadTimeout,WriteTimeout: setting.WriteTimeout,MaxHeaderBytes: 1 << 20,}s.ListenAndServe()
}
主函数工作
- 初始化路由
- server 配置
- 启动
router 路由模块
func InitRouter() *gin.Engine {r := gin.New()r.Use(gin.Logger())r.Use(gin.Recovery())gin.SetMode(setting.RunMode)r.GET("/auth", api.GetAuth)apiv1 := r.Group("/api/v1")apiv1.Use(jwt.JWT()){//获取标签列表apiv1.GET("/tags", v1.GetTags)//新建标签apiv1.POST("/tags", v1.AddTag)//更新指定标签apiv1.PUT("/tags/:id", v1.EditTag)//删除指定标签apiv1.DELETE("/tags/:id", v1.DeleteTag)//获取文章列表apiv1.GET("/articles", v1.GetArticles)//获取指定文章apiv1.GET("/articles/:id", v1.GetArticle)//新建文章apiv1.POST("/articles", v1.AddArticle)//更新指定文章apiv1.PUT("/articles/:id", v1.EditArticle)//删除指定文章apiv1.DELETE("/articles/:id", v1.DeleteArticle)}return r
}
主要工作
- 创建 gin 对象
- 添加 Logger、Recovery 中间件
- Logger 日志处理
- Recovery 异常捕获
- 设置 /auth 路径的处理器
- 设置 /api/v1 group,通过 JWT 进行授权验证
- 设置 /api/v1 下各个请求的处理方式
auth 授权模块
type auth struct {Username string `valid:"Required; MaxSize(50)"`Password string `valid:"Required; MaxSize(50)"`
}func GetAuth(c *gin.Context) {username := c.Query("username")password := c.Query("password")valid := validation.Validation{}a := auth{Username: username, Password: password}ok, _ := valid.Valid(&a)data := make(map[string]interface{})code := e.INVALID_PARAMSif ok {isExist := models.CheckAuth(username, password)if isExist {token, err := util.GenerateToken(username, password)if err != nil {code = e.ERROR_AUTH_TOKEN} else {data["token"] = tokencode = e.SUCCESS}} else {code = e.ERROR_AUTH}} else {for _, err := range valid.Errors {logging.Info(err.Key, err.Message)}}c.JSON(http.StatusOK, gin.H{"code" : code,"msg" : e.GetMsg(code),"data" : data,})
}
工作:
- 获取用户 username password
- 验证 username password 是否符合格式
- 符合,通过 models 进行授权检查,组装 data
- 不符合,打印错误日志
- 使用 JSON 格式返回
数据库
数据库信息的设置在 models 文件夹下,主要包括 model 文件以及其他具体表文件。
- model.go 模块通用参数、全局数据库对象、初始化方法&&数据库关闭方法
- article.go article 对象及其操作方法
重点看一下 model 文件。
var db *gorm.DBtype Model struct {ID int `gorm:"primary_key" json:"id"`CreatedOn int `json:"created_on"`ModifiedOn int `json:"modified_on"`
}func init() {var (err errordbType, 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()
}
组成部分
- 全局变量 db,用于操作数据库
- Model 结构体,各个数据库表的通用字段
- init 方法 && closeDB 方法:数据库初始化以及关闭方法
从中可知,db 主要是通过使用 gorm 框架进行操作,操作中需要设置数据库相关参数以及最大连接数。
models 下其他具体的数据库表信息在此不做赘述,基本就是数据库表的 struct 定义以及相应操作,下面贴出该项目涉及的三个数据库表及其字段。
type Article struct {ModelTagID int `json:"tag_id" gorm:"index"`Tag Tag `json:"tag"`Title string `json:"title"`Desc string `json:"title"`Content string `json:"content"`CreatedBy string `json:"created_by"`ModifiedBy string `json:"modified_by"`State int `json:"state"`
}type Tag struct {ModelName string `json:"name"`CreatedBy string `json:"created_by"`ModifiedBy string `json:"modified_by"`State int `json:"state"`
}type Auth struct {ID int `gorm:"primary_key" json:"id"`Username string `json:"username"`Password string `json:"password"`
}
修改文章请求分析
主要模块已经了解后,我们查看修改文章 api 的请求过程和具体实现。
- 请求 URL:/articles/:id
- 请求方法 :PUT
- 处理函数: v1.EditArticle
上述主要代码模块,已经了解到 main 函数启动后,会初始化路由,路由中包含了「修改文章」请求的具体处理函数,这里看看具体函数操作。
func EditArticle(c *gin.Context) {valid := validation.Validation{}id, _ := com.StrTo(c.Param("id")).Int()tagId, _ := com.StrTo(c.Query("tag_id")).Int()title := c.Query("title")desc := c.Query("desc")content := c.Query("content")modifiedBy := c.Query("modified_by")var state int = -1if arg := c.Query("state"); arg != "" {state, _ = com.StrTo(arg).Int()valid.Range(state, 0, 1, "state").Message("状态只允许0或1")}valid.Min(id, 1, "id").Message("ID必须大于0")valid.MaxSize(title, 100, "title").Message("标题最长为100字符")valid.MaxSize(desc, 255, "desc").Message("简述最长为255字符")valid.MaxSize(content, 65535, "content").Message("内容最长为65535字符")valid.Required(modifiedBy, "modified_by").Message("修改人不能为空")valid.MaxSize(modifiedBy, 100, "modified_by").Message("修改人最长为100字符")code := e.INVALID_PARAMSif ! valid.HasErrors() {if models.ExistArticleByID(id) {if models.ExistTagByID(tagId) {data := make(map[string]interface {})if tagId > 0 {data["tag_id"] = tagId}if title != "" {data["title"] = title}if desc != "" {data["desc"] = desc}if content != "" {data["content"] = content}data["modified_by"] = modifiedBymodels.EditArticle(id, data)code = e.SUCCESS} else {code = e.ERROR_NOT_EXIST_TAG}} else {code = e.ERROR_NOT_EXIST_ARTICLE}} else {for _, err := range valid.Errors {logging.Info(err.Key, err.Message)}}c.JSON(http.StatusOK, gin.H{"code" : code,"msg" : e.GetMsg(code),"data" : make(map[string]string),})
}// models article.go
func EditArticle(id int, data interface {}) bool {db.Model(&Article{}).Where("id = ?", id).Updates(data)return true
}
工作:
- 参数获取
- 设置校验规则并进行校验
- 校验成功
- 文章是否已经存在
- 是,构建 data 参数,通过 models EditAriticle 修改,具体修改逻辑:找到主键等于 id 的数据,并通过 update 进行更新。
- 否,设置文章不存在错误码
- 文章是否已经存在
- 校验失败
- 打印错误日志
- JSON 响应
- 校验成功
其他依赖
- go-ini 库
- 该项目通过 ini 文件进行配置管理,go-ini 是 Go 语言中用于操作 ini 文件的第三方库。
- beego
- 另一个 go web 框架,项目中主要使用了 beego 的vaild 功能
- gorm
- go ORM 框架
- go-vendor
- 该项目第一次提交为 18 年,通过 vendor 来管理依赖,现在 go mod 诞生后,这种方式已被放弃
总结
该项目第一版提交,大体上完成了 blog 项目所需的基本功能,在目录结构上也相对清晰。
不足:
- 返回信息的 Code、Msg 对象设计比较一般。每次响应需要自己构建响应格式{code;msg;data}。
- 配置管理相对粗糙,直接读取配置文件而不是通过 global 统一管理调度。
相关文章:
「项目阅读系列」go-gin-example star 6.5k!(1)
文章目录 准备工作适宜人群项目信息 项目结构代码阅读主要模块代码主函数模块router 路由模块auth 授权模块数据库 修改文章请求分析其他依赖 总结 准备工作 适宜人群 初学 go 语法,希望了解 go 项目的构建过程和方式。 项目信息 go-gin-example 项目是使用 gin…...
基于轻量级yolov5的瓷砖瑕疵检测系统
该专栏仅支持购买本专栏的同学学习使用,不支持以超级会员、VIP等形式使用,请谅解!【购买专栏后可选择其中一个完整源码项目】 本文是我新开设的专栏《完整源码项目实战》 的第十二篇全源码文章,包含数据集在内的所有资源,可以实现零基础上手入门学习。前面系列文章链接如下…...
Linux:系统基本信息扫描(2)
#网络地址:ip a------------------------------------------------------------- ip a echo "主机名:-------------------------------------------------------" hostnamectl sleep 0.5s echo "#系统基本信息:--------------------------------------------…...
什么是虚拟DOM(Virtual DOM),说说工作原理
虚拟DOM(Virtual DOM)是一种将页面的状态抽象为JavaScript对象表示的概念,用于提高Web应用程序的性能和渲染效率。 虚拟DOM的工作原理如下: 1: 初始渲染:首先,通过JavaScript对象(…...
Kafka的重要组件,谈谈流处理引擎Kafka Stream
系列文章目录 上手第一关,手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么,以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 Kafka存取原理与实现分析,打破面试难关 防止消息丢失与消息重复——Kafka可…...
基于yolov5模型的200种鸟类检测识别分析系统
该专栏仅支持购买本专栏的同学学习使用,不支持以超级会员、VIP等形式使用,请谅解!【购买专栏后可选择其中一个完整源码项目】 本文是我新开设的专栏《完整源码项目实战》 的第十三篇全源码文章,包含数据集在内的所有资源,可以实现零基础上手入门学习。前面系列文章链接如下…...
JavaScript的学习,就这一篇就OK了!(超详细)
目录 Day27 JavaScript(1) 1、JS的引入方式 2、ECMAScript基本语法 3、ECMAScript 基本数据类型编辑 3.1 数字类型 3.2 字符串 3.3 布尔值 3.4 空值(Undefined和Null) 3.5 类型转换 3.6 原始值和引用值 4、运算符 5、流程控制语句 5.1 分…...
hive sql 取当周周一 str_to_date(DATE_FORMAT(biz_date, ‘%Y%v‘), ‘%Y%v‘)
select str_to_date(DATE_FORMAT(biz_date, %Y%v), %Y%v)方法拆解 select DATE_FORMAT(now(), %Y%v), str_to_date(202346, %Y%v)...
【React】React 基础
1. 搭建环境 npx create-react-app react-basic-demo2. 基本使用 JSX 中使用 {} 识别 JavaScript 中的表达式,比如变量、函数调用、方法调用等。 if、switch、变量声明等属于语句,不是表达式。 列表渲染使用 map 。 事件绑定用;on 事件名称…...
CentOS7 设置 nacos 开机启动
1、新增服务文件 vim /lib/systemd/system/nacos.service2、增加如下内容 [Unit] Descriptionnacos Afternetwork.target[Service] Typeforking ExecStart/usr/local/nacos/bin/startup.sh -m standalone ExecReload/usr/local/nacos/bin/shutdown.sh ExecStop/usr/local/nac…...
使用低代码可视化开发平台快速搭建应用
目录 一、JNPF可视化平台介绍 二、搭建JNPF可视化平台 【表单设计】 【报表设计】 【流程设计】 【代码生成器】 三、使用JNPF可视化平台 1.前后端分离: 2.多数据源: 3.预置功能: 4.私有化部署: 四、总结 可视化低代码…...
数据分析思维与模型:多维度拆解分析法
多维度拆解分析法"(Multi-Dimensional Analysis and Decomposition Method)是一种用于深入分析和解决复杂问题的方法论。这种方法侧重于从多个角度或维度来考察问题,以便于更全面地理解和解决它们。它通常包括以下几个步骤: …...
Goby 漏洞发布|大华智慧园区综合管理平台 poi 文件上传漏洞
漏洞名称:大华智慧园区综合管理平台 poi 文件上传漏洞 English Name:Dahua Smart Park Integrated Management Platform poi file upload vulnerability CVSS core:9.0 影响资产数:7113 漏洞描述: 大华智慧园区综合管理平台是…...
视频修复软件 Aiseesoft Video Repair mac中文版功能
AIseesoft Video RepAIr mac是一款专业的视频修复软件,主要用于修复损坏或无法播放的视频文件。AIseesoft Video RepAIr是一个功能强大的程序,可以帮助恢复丢失或损坏的数据的视频。只要您以相同的格式提供示例视频,并在功能强大的技术的支持下,只需单击几下即可收获…...
企业spark案例 —— 出租车轨迹分析(Python)
第1关:SparkSql 数据清洗 # -*- coding: UTF-8 -*- from pyspark.sql import SparkSession if __name__ __main__:spark SparkSession.builder.appName("demo").master("local").getOrCreate()#**********begin**********#df spark.read.opt…...
SQL Server - 使用 Merge 语句实现表数据之间的对比同步
在SQL server (2008以上版本)中当需要将一个表(可能另一个库)中数据同步到另一个表中时,可以考虑使用merge语句。 只需要提供: 1.目标表 (target table) 2.数据源表 (source table) …...
【Web】Flask|Jinja2 SSTI
目录 ①[NISACTF 2022]is secret ②[HNCTF 2022 WEEK2]ez_SSTI ③[GDOUCTF 2023] ④[NCTF 2018]flask真香 ⑤[安洵杯 2020]Normal SSTI ⑥[HNCTF 2022 WEEK3]ssssti ⑦[MoeCTF 2021]地狱通讯 ①[NISACTF 2022]is secret dirsearch扫出/secret 明示get传一个secret ?…...
SPDK NVMe-oF target多路功能介绍
基本概念 SPDK NVMe-oF target multi-path是基于NVMe协议的multi-path IO和namespace sharing功能。 NVMe multi-path IO指的是两个或多个完全独立的PCI Express路径存在于一个主机和一个命名空间。 而namespace 共享是两个或多个主机使用不同的NVMe控制器访问一个shared na…...
ADAudit Plus:助力企业安全的权威选择
在当今数字化的时代,信息安全已经成为企业发展的头等大事。随着网络攻击和数据泄露的频繁发生,企业需要一种全面的解决方案来保护其关键业务数据和敏感信息。ADAudit Plus作为一款强大的安全审计软件,为企业提供了完整的安全解决方案…...
sqli-labs关卡18(基于http头部报错盲注)通关思路
文章目录 前言一、靶场通关需要了解的知识点1、什么是http请求头2、为什么http头部可以进行注入 二、靶场第十八关通关思路1、判断注入点2、爆数据库名3、爆数据库表4、爆数据库列5、爆数据库关键信息 总结 前言 此文章只用于学习和反思巩固sql注入知识,禁止用于做…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
