Go语言Gin框架的常规配置和查询数据返回json示例
文章目录
- 路由文件分组
- 查询数据库并返回json
- service层
- controller
- 路由
- 运行效果
- 启动多个服务
在 上一篇文章《使用Go语言的gorm框架查询数据库并分页导出到Excel实例》 中主要给大家分享了较多数据的时候如何使用go分页导出多个Excel文件并合并的实现方案,这一篇文章继续分享一下go语言的Gin框架的一些常规配置和业务中常用的查询数据库并返回json的实现方案。
Gin是一个golang的微框架,基于 httprouter,具有快速灵活,容错率高,高性能等特点。借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范。
Gin框架官网:https://gin-gonic.com/zh-cn/,新增一个Go文件,引入 github.com/gin-gonic/gin 即可使用Gin框架。
路由文件分组
正常情况下,Gin框架通过如下代码即可快速实现一个路由和方法:
// router/router.go
package routerfunc Router() *gin.Engine {r := gin.Default()r.GET("/json", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"success": true,})})
}// main.go
package mainimport ("go-demo-2025/router"
)
func main() {r := router.Router()r.Run(":8080") // listen and serve on 0.0.0.0:8080
}
但是,随着项目接口的不断增多,如果把所有的路由都写在一个文件里面的话,不易维护。因此,可以在项目一开始就对路由分成多个文件。实现如下:
- 客户相关路由:
router/customer.go
package routerfunc CustomerRouter(e *gin.Engine) {customer := e.Group("/customer"){customer.GET("/list", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"success": "获取客户列表",})})customer.GET("/info", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"success": "获取客户详情",})}) }
}
- 订单相关路由:
router/order.go
package router// Order 路由
func OrderRouter(e *gin.Engine) {order := e.Group("/order"){order.GET("/list", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"success": "获取订单列表",})})order.GET("/info", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"success": "获取订单详情",})})}// 可以设定多层路由分组orders := e.Group("/orders"){ordersTeacher := orders.Group("/ordersHistory"){ordersTeacher.GET("/list", func(c *gin.Context) { //访问: http://127.0.0.1:8080/orders/ordersHistory/listc.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"success": "/orders/ordersHistory/list",})})}}
}
- 修改
main.go文件:
// main.go
package mainimport ("go-demo-2025/router"
)
func main() {r := router.Router()router.CustomerRouter(r)router.OrderRouter(r)r.Run(":8080") // listen and serve on 0.0.0.0:8080
}
运行效果:




查询数据库并返回json
service层
在 上一篇文章 中,我们已经通过 gorm_generate_db_struct.go 自动生成了数据表的结构体:
type User struct {ID int64 `gorm:"column:id;type:int(11) unsigned;primaryKey;autoIncrement:true;comment:ID" json:"id"` // IDUserID int64 `gorm:"column:user_id;type:bigint(20) unsigned;not null;comment:用户编号" json:"user_id"` // 用户编号Name string `gorm:"column:name;type:varchar(255);not null;comment:用户姓名" json:"name"` // 用户姓名Age int64 `gorm:"column:age;type:tinyint(4) unsigned;not null;comment:用户年龄" json:"age"` // 用户年龄Address string `gorm:"column:address;type:varchar(255);not null;comment:地址" json:"address"` // 地址CreateTime time.Time `gorm:"column:create_time;type:datetime;not null;default:CURRENT_TIMESTAMP;comment:添加时间" json:"create_time"` // 添加时间UpdateTime time.Time `gorm:"column:update_time;type:datetime;not null;default:CURRENT_TIMESTAMP;comment:更新时间" json:"update_time"` // 更新时间
}
那么,在service层直接进行查询操作即可。gorm框架支持同时查询列表和总数,这一点非常好。
参考文档:
https://gorm.io/zh_CN/docs/query.html
https://www.cnblogs.com/rainbow-tan/p/15822714.html
首先,定义两个结构体,分别用来处理 客户端请求的参数 和 服务端返回的结构:
// 获取用户列表, 客户端的请求参数
type UserListRequest struct {Address string `json:"address" binding:"required"` //用户地址关键词,假设此处要求检索的时候必填Name string `json:"name"` //用户姓名common.CommonListRequest
}// 获取用户列表, 服务端的响应结构体, 在原有的数据表结构的基础上进行扩展
type UserListResponse struct {model.UserNamePinyin string `json:"name_pinyin"` //姓名拼音AgeDesc string `json:"age_desc"` //年龄描述
}
在 service/users/userService.go 中的查询逻辑代码如下:
func (ctx *UserService) QueryUserList(params UserListRequest) ([]UserListResponse, int64) {//查询条件//fmt.Println(params)where := "1=1"if params.Address != "" {where += " and address like '%" + params.Address + "%'"}if params.Name != "" {where += " and name = '" + params.Name + "'"}//查询总数和列表var dataList []UserListResponsevar count int64page := params.Page //当前第几页pageSize := params.PageSize //每页查询多少条offset := (page - 1) * pageSize //偏移量err := ctx.GormDB.Model(&model.User{}).Select("*").Where(where).Order("id desc").Limit(pageSize).Offset(offset).Scan(&dataList).Limit(-1).Offset(-1).Count(&count).Errorif err != nil {fmt.Println(fmt.Sprintf("数据库查询错误:%s", err))return nil, 0}fmt.Println(fmt.Sprintf("总条数:%d", count))for k, v := range dataList { //这里简单示例 对查询的结果进行二次处理var ageDesc stringif v.Age >= 18 {ageDesc = "成年"} else {ageDesc = "未成年"}dataList[k].AgeDesc = ageDescdataList[k].NamePinyin = common.ConvertChineseToPinyin(v.Name)}return dataList, count
}
controller
接下来,将service层查询到的结果返回给 controller进一步处理:
//controllers/customerController/customer.gofunc GetCustomerList(c *gin.Context) {//入参校验var requestData users.UserListRequesterr := c.Bind(&requestData) //执行绑定//fmt.Println("获取客户端请求的参数:", requestData)if err != nil {controllers.ReturnError(c, 1001, fmt.Sprintf("请求参数错误: %s", err))return}//调用service查询数据service := users.NewUserService()dataList, count := service.QueryUserList(requestData)//自定义要返回的字段showFields := []string{"id", "name", "name_pinyin", "age", "age_desc", "address"}var resultList []map[string]anyfor _, item := range dataList {//fmt.Println(item)itemMap := funcUtils.ConvertToFlatMap(item, "") // 通过反射将嵌套结构体转换为一维 map//fmt.Println(itemMap)itemData := make(map[string]any)for _, field := range showFields {itemData[field] = itemMap[field]}resultList = append(resultList, itemData)}controllers.ReturnSuccess(c, 200, "success", resultList, int(count))
}
- 通过
err := c.Bind(&requestData)将客户端传来的参数和结构体的字段进行绑定 showFields中自定义了需要返回给客户端的字段funcUtils.ConvertToFlatMap(item, "")将数据表的结构体转换为map,为了方便和上面的showFields进行比对,并且不需要再额外定义新的结构体了(go里面动不动就要定义结构体,确实挺烦人的,干脆转为map处理通用业务逻辑,方便省事!)。需要注意的是,由于我们前面定义的服务端返回数据结构体采用了结构体嵌套的形式:
type UserListResponse struct {model.UserNamePinyin string `json:"name_pinyin"` //姓名拼音AgeDesc string `json:"age_desc"` //年龄描述
}
因此,这里通过结构体转换map的时候,需要使用反射和递归的思路去处理,核心代码如下:
// 通过反射将嵌套结构体转换为一维 map
func ConvertToFlatMap(obj interface{}, prefix string) map[string]interface{} {val := reflect.ValueOf(obj)result := make(map[string]interface{})// 递归处理结构体flatten(val, prefix, &result)return result
}// 递归处理结构体
func flatten(val reflect.Value, prefix string, result *map[string]interface{}) {// 如果当前值是结构体类型if val.Kind() == reflect.Struct {for i := 0; i < val.NumField(); i++ {field := val.Type().Field(i)fieldValue := val.Field(i)// 检查字段是否导出if field.PkgPath == "" {//newPrefix := field.NamenewPrefix := field.Tag.Get("json")// 递归处理子字段flatten(fieldValue, newPrefix, result)}}} else if val.Kind() == reflect.Slice {// 如果当前值是切片类型for i := 0; i < val.Len(); i++ {elem := val.Index(i)// 递归处理切片中的元素newPrefix := strconv.Itoa(i)flatten(elem, newPrefix, result)}} else {// 如果当前值不是结构体或切片类型(*result)[prefix] = val.Interface()}
}
路由
在上面定义好的一个路由文件中添加相关路由入口: router/customer.go
package routerfunc CustomerRouter(e *gin.Engine) {customer.POST("/list", customerController.GetCustomerList)}
}
运行效果


启动多个服务
示例代码如下:
// run_multiple_server.go// 运行多个服务
package mainimport ("fmt""github.com/gin-gonic/gin""golang.org/x/sync/errgroup""net/http""time"
)var g errgroup.Groupfunc main() {//服务器1:http://127.0.0.1:8081/server01 := &http.Server{Addr: ":8081",Handler: router01(),ReadTimeout: 5 * time.Second, //读取超时时间WriteTimeout: 10 * time.Second, //写入超时时间}//服务器2:http://127.0.0.1:8082/server02 := &http.Server{Addr: ":8082",Handler: router02(),ReadTimeout: 5 * time.Second,WriteTimeout: 10 * time.Second,}//开启服务g.Go(func() error { //开启服务器程序1return server01.ListenAndServe()})g.Go(func() error { //开启服务器程序2return server02.ListenAndServe()})//让监听程序一直处于等待状态if err := g.Wait(); err != nil {fmt.Println("执行失败:", err)}
}func router01() http.Handler {r1 := gin.Default()r1.GET("/", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"msg": "服务器01的响应",},)})return r1
}func router02() http.Handler {r1 := gin.Default()r1.GET("/", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"msg": "服务器02的响应",},)})return r1
}//解决包:golang.org/x/sync/errgroup 无法 go get的问题
// cd $GOPATH/src/golang.org/x
// git clone https://github.com/golang/sync.git
// git clone https://github.com/golang/crypto.git
// git clone https://github.com/golang/sys.git
此时,两个站点可以同时访问:

完整源代码:https://gitee.com/rxbook/go-demo-2025
相关文章:
Go语言Gin框架的常规配置和查询数据返回json示例
文章目录 路由文件分组查询数据库并返回jsonservice层controller路由运行效果 启动多个服务 在 上一篇文章《使用Go语言的gorm框架查询数据库并分页导出到Excel实例》 中主要给大家分享了较多数据的时候如何使用go分页导出多个Excel文件并合并的实现方案,这一篇文章…...
JavaEE----多线程(二)
文章目录 1.进程的状态2.线程的安全引入3.线程安全的问题产生原因4.synchronized关键字的引入4.1修饰代码块4.2修饰实例方法4.3修饰静态方法4.4对象头介绍4.5死锁-可重入的特性 5.关于死锁的分析总结5.1死锁的分析5.2死锁成因的必要条件5.3死锁的解决方案 1.进程的状态 public…...
【K8S】快速入门Kubernetes
之前企业都是使用容器化和来构建自己的服务和应用程序,其中容器化优点有很多:提升了部署效率、稳定性、提高了资源的利用率降低了成本。 但是也带来了一些新的问题:容器的数量变得很多,管理就是一个新的问题。所以Kubernetes就出…...
如何在 MySQL 中处理大量的 DELETE 操作??
全文目录: 开篇语前言摘要简介概述DELETE 操作的基本概念常用的 DELETE 方法 核心源码解读简单 DELETE 语句批量 DELETE 示例 案例分析案例1:使用简单 DELETE 删除用户数据案例2:使用分批 DELETE 应用场景演示场景1:用户管理系统场…...
LabVIEW中句柄与引用
在LabVIEW中,句柄(Handle) 是一种用于引用特定资源或对象的标识符。它类似于指针,允许程序在内存中管理和操作复杂的资源,而不需要直接访问资源本身。句柄用于管理动态分配的资源,如队列、文件、网络连接、…...
【三十四】【QT开发应用】音量图标以及滑动条,没有代码补全的小技巧
效果展示 鼠标位于音量图标区域内,显示出滑动条。鼠标移出音量图标区域内滑动条隐藏。鼠标点击音量图标,如果此时音量为0,音量变成50,如果此时音量不为零,音量变为0。 CVolumeButton.h 音量图标头文件 #pragma once …...
Android修改第三方应用相机方向
以下修改基于Android7.1 diff --git a/frameworks/base/core/java/android/hardware/Camera.java b/frameworks/base/core/java/android/hardware/Camera.java index 8c7434b..7201481 100755 --- a/frameworks/base/core/java/android/hardware/Camera.java b/frameworks/ba…...
Python 读取文件汇总
readline和readlines的区别 使用 open()读取文件时,readline是读取文件的一行;而readlines是加载全部文档,以list形式保存每一行内容。 使用with避免资源泄露 with语句不仅限于open()函数,任何实现了上下文管理协议的对象都可以…...
云原生:一张图了解devops 中CI/CD
一个典型的云原生应用的开发和部署过程,其中涉及到的主要工具有 Git、Docker、Jenkins/CircleCI、Ansible、Kubernetes 等。以下是每个步骤的简要说明: 开发人员(Developers)使用 Git 进行版本控制,他们将代码推送到 G…...
无人机之自组网通信技术篇
无人机的自组网通信技术是一种利用无人机作为节点,通过无线通信技术实现节点间自主组网、动态路由和数据传输的技术。 一、技术原理与特点 技术原理:无人机自组网技术基于自组织网络(Ad-Hoc Network)的原理,通过无线…...
【WebLogic】Oracle发布2024年第四季度中间件安全公告
Oracle于美国时间2024年10月15日发布了 WebLogic 12c(12.2.1.4.0)和14c(14.1.1.0.0)两个大版本2024年第4季度的安全公告,涉及漏洞ID共计 6 个,包含2个高危漏洞 2 个,4个中危漏洞,其中…...
Java集合(3:Set和Map)
文章目录 Set概述哈希值HashSet去重原理LinkedHashSetTreeSet自定义排序规则 Map概述Map的基本方法Map集合的获取功能哈希表HashMap底层源码 特点注意 Set 概述 Set集合也是一个接口,继承自Collection,与List类似,都需要通过实现类来进行操…...
【Golang】Gin框架中如何定义路由
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
CPU内存飙升
CPU 飙升介绍 CPU 飙升是指中央处理器(CPU)的使用率在短时间内急剧上升,达到一个较高的水平。正常情况下,CPU 会根据系统和应用程序的需求合理分配资源,使用率会在一定范围内波动。但当 CPU 飙升时,可能会导…...
【Java】LinkedList实现类的使用
LinkedList实现类的使用 package com.star.test04;import java.util.Iterator;import java.util.LinkedList;/** * author : Starshine */public class Test { //这是main方法,程序的入口 public static void main(String[] args) { /* LinkedL…...
创建人物状态栏
接下来,我们来尝试制作一下我们的UI,我们会学习unity基本的UI系统 ************************************************************************************************************** 我们要先安装一个好用的插件到我们的unity当中,帮助…...
django5入门【01】环境配置
注意: ⭐前提:安装了annaconda(python版本管理工具),如果没有安装,强烈建议安装一下!!!操作: 前言: 这里新创建一个名为“python_3.11_start_dja…...
1000集《楼兰》系列短剧开机仪式在疆举行,开启全球传播新篇章
2024年10月18日,光明媚,秋风送爽。 在这个收获的季节里,倍受期待的楼兰系列短剧《楼兰之天女归来》和《楼兰之时空秘宝》在新疆吐鲁番东方红卓览文化博物馆举行了隆重的开机仪式,正式拉开了摄制的序幕。 1000集《楼兰》系列短剧…...
【景观生态学实验】实验五 景观生态脆弱性评价
实验目的 1.学习层次分析模型思路,对丹江口库区2000年景观生态脆弱性评价建模:通过实验课的学习,深入理解层次分析(Analytic Hierarchy Process,AHP)理论与模型,了解其在决策问题中的应用&…...
ChatGPT 现已登陆 Windows 平台
今天,OpenAI 宣布其人工智能聊天机器人平台 ChatGPT 已开始预览专用 Windows 应用程序。OpenAI 表示,该应用目前仅适用于 ChatGPT Plus、Team、Enterprise 和 Edu 用户,是一个早期版本,将在今年晚些时候推出"完整体验"。…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
