Go语言Gin框架的常规配置和查询数据返回json示例

ops/2024/10/15 22:21:34/

文章目录

  • 路由文件分组
  • 查询数据库并返回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_128">查询数据库并返回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


http://www.ppmy.cn/ops/126143.html

相关文章

git pull

# git reset --hard # git clean -f #git pull git pull origin master

MySQL事务、存储引擎

目录 一、事务 1.1 事务的概念 1.2 事务的ACID特点 1.2.1 原子性 1.2.2 一致性 1.2.3 隔离性 1.2.4 持久性 1.3 事务控制语句 1.4 使用 set 设置控制事务 二、存储引擎 2.1 存储引擎的定义 2.2 常用的存储引擎 2.3 存储引擎的管理操作 三、死锁 3.1 定义 3.2 死…

【git】本地玩坏了不必重新clone

#没错&#xff0c;事情发生的起因就是我把本地玩坏了……然后傻乎乎地打算rm掉重新再clone&#xff0c;巨慢真的&#xff0c;然后我就又被涨知识了&#xff0c;分享一下&#xff0c;如果已经知道的就笑笑路过吧哈哈哈# 场景 需要重新clone的&#xff0c;恢复到与远端仓库一致…

站在用户视角审视:以太彩光与PON之争

作者:科技作家-郑凯 园区,是企业数字化转型的“中心战场”。 云计算、大数据、人工智能等数智化技术在园区里“战火交织”;高清视频、协同办公,智慧安防等大量创新应用产生的海量数据在园区内“纵横驰骋”;加上大量的IOT和智能化设备涌入“战场”,让园区网络面对着难以抵御的…

【Java 问题】集合——List

List 1.说说有哪些常见集合&#xff1f;2.ArrayList和LinkedList有什么区别&#xff1f;3.ArrayList的扩容机制了解吗&#xff1f;4.ArrayList怎么序列化的知道吗&#xff1f; 为什么用transient修饰数组&#xff1f;5.快速失败(fail-fast)和安全失败(fail-safe)了解吗&#xf…

代码随想录算法训练营第三十五天|452. 用最少数量的箭引爆气球 435. 无重叠区间 763.划分字母区间

452. 用最少数量的箭引爆气球 在二维空间中有许多球形的气球。对于每个气球&#xff0c;提供的输入是水平方向上&#xff0c;气球直径的开始和结束坐标。由于它是水平的&#xff0c;所以纵坐标并不重要&#xff0c;因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结…

Java 包装器一口气讲完!(≧∇≦)ノ

目录 Java 数据类型包装器 Java数据类型教程 - Java数据类型包装器 方法 例子 valueOf() Java 数字数据类型 Java数据类型教程 - Java数字数据类型 例子 方法 值 Java 字符数据类型 Java数据类型教程 - Java字符数据类型 例子 Java Boolean包装类 Java数据类型教程…

windows C++-避免死锁(下)

使用 join 防止死锁 下面介绍了如何使用消息缓冲区和消息传递函数来消除死锁的可能性。 为了将该示例与上一示例相关联&#xff0c;philosopher 类通过使用 concurrency::unbounded_buffer 对象和 join 对象来替换每个 critical_section 对象。 join 对象充当为哲学家提供筷子…