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 router

func 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 main

import (
	"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 router

func 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/list
				c.JSON(http.StatusOK, gin.H{
					"code":    http.StatusOK,
					"success": "/orders/ordersHistory/list",
				})
			})
		}
	}
}
  • 修改 main.go 文件:
// main.go
package main

import (
	"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"`                  // ID
	UserID     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.User
	NamePinyin 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 []UserListResponse
	var count int64
	page := 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).Error
	if err != nil {
		fmt.Println(fmt.Sprintf("数据库查询错误:%s", err))
		return nil, 0
	}

	fmt.Println(fmt.Sprintf("总条数:%d", count))
	for k, v := range dataList { //这里简单示例 对查询的结果进行二次处理
		var ageDesc string
		if v.Age >= 18 {
			ageDesc = "成年"
		} else {
			ageDesc = "未成年"
		}
		dataList[k].AgeDesc = ageDesc
		dataList[k].NamePinyin = common.ConvertChineseToPinyin(v.Name)
	}
	return dataList, count
}

controller

接下来,将service层查询到的结果返回给 controller进一步处理:

//controllers/customerController/customer.go

func GetCustomerList(c *gin.Context) {
	//入参校验
	var requestData users.UserListRequest
	err := 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]any
	for _, 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.User
	NamePinyin 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.Name
				newPrefix := 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 router

func CustomerRouter(e *gin.Engine) {
		customer.POST("/list", customerController.GetCustomerList)
	}
}

运行效果

在这里插入图片描述

在这里插入图片描述

启动多个服务

示例代码如下:

// run_multiple_server.go

// 运行多个服务
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
	"net/http"
	"time"
)

var g errgroup.Group

func 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 { //开启服务器程序1
		return server01.ListenAndServe()
	})
	g.Go(func() error { //开启服务器程序2
		return 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux之远程连接服务器

远程连接服务器的类型 文字接口 明文传输&#xff1a;Telnet 23、RSH等&#xff0c;目前非常少用&#xff1b; 加密传输&#xff1a;SSH为主&#xff0c;已经取代明文传输 ssh提供两个服务器功能&#xff1a;1.类似于telnet&#xff1b;2.类似于ftp的sftp-serve…

特斯拉自动驾驶出租车计划变成泡影?联想与Meta合作,推出面向PC的个人AI智能体AI Now|AI日报

文章推荐 Swarms Corporation创始人Kye Gomez实锤OpenAI多智能体Swarm抄袭其成果&#xff01;&#xff5c;AI日报 今日热点 中国海油“海能”人工智能模型正式发布 近日&#xff0c;由中国海油与中国电信、科大讯飞等企业合作打造“海能”人工智能模型正式推出。 中国海油“…

Centos7搭建minio对象存储服务器

Centos7搭建minio对象存储服务器 安装二进制程序配置服务文件 安装二进制程序 参考&#xff1a;https://segmentfault.com/q/1010000042181876 minio中国版&#xff1a;https://www.minio.org.cn/download.shtml#/linux # 下载二进制程序 wget https://dl.min.io/server/min…

鸿蒙--应用首次启动

最终效果 前言 基于自定义弹框、首选项和页面路由实现一个模拟应用首次启动的案例。需要完成以下功能: 实现四个页面,启动页、隐私协议页、广告页和应用首页。实现自定义隐私协议弹窗,点击协议可查看隐私协议具体内容。页面间的路由跳转。相关概念 首选项:首选项为应用提供…

软件工程:图书管理系统甘特图

1 实验目的 熟悉GanttProject 软件环境&#xff0c;能够使用GanttProject绘制甘特图,进行项目管理与规划。 2 实验内容 为小型图书管理系统项目的实施计划绘制甘特图。 小型图书管理系统项目包含登录、浏览、管理读者、管理图书资料、管理书目、登记借书、登记还书、预定图书、…

Snort浅析

Snort简介 Snort是免费开源的IDS/IPS&#xff08;入侵检测/防御系统&#xff09;系统&#xff0c;于1998年开发&#xff0c;旨在检测和响应网络中的可疑活动。包含流量/协议分析、内容匹配等功能&#xff0c;并可用预定义规则检测和防止各种攻击。官方网站&#xff1a;https:/…

出口摩洛哥提示 | 燃气器具和设备,2024年12月20日起需要标识Cmim Mark

Cmim Mark 为了证明产品符合摩洛哥的技术法规及标准&#xff0c;指导消费者正确选购&#xff0c;并协助政府有效管理市场&#xff0c;所有依据第24-09号法律规定的产品&#xff0c;必须加贴清晰的Cmim Mark&#xff0c;方可顺利进入摩洛哥市场。 根据摩洛哥官方公报发布的关于…

K歌与露营最搭配,AISON爱畅K歌音箱让露营更有趣

据市场调研数据显示&#xff0c;中国露营经济核心市场规模和带动市场规模均呈现逐年上升趋势&#xff0c;预计到2025年&#xff0c;中国露营经济核心市场规模将达到2483.2亿元。同时&#xff0c;《2024小红书搜索推广白皮书》显示&#xff0c;城市出行、音乐、旅游和户外等娱乐…

redis的配置文件redis.conf解析

我的后端学习大纲 我的Redis学习大纲 1.1.Redis的配置文件&#xff1a; 1.Redis的配置文件名称是&#xff1a;redis.conf 2.在vim这个配置文件的时候&#xff0c;默认是不显示行号的&#xff0c;可以编辑下面这个文件&#xff0c;末尾加上set nu&#xff0c;就会显示行号: 1.…

STM32应用详解(5)USART串口初始化

文章目录 一、USART初始化二、代码说明1.原理图2.main函数3.USART串口初始化函数4.代码整体结构 三、USART串口初始化总结 一、USART初始化 所谓的对USART进行初始化&#xff0c;就是对USART固件库函数的调用&#xff0c;来完成串口(USART)的设置&#xff0c;比如设置波特率、…

Docker 搭建mysql

拉取mysql镜像 docker pull mysql # 拉取镜像 [rooteason ~]# docker pull mysql Using default tag: latest latest: Pulling from library/mysql 72a69066d2fe: Pull complete 93619dbc5b36: Pull complete 99da31dd6142: Pull complete 626033c43d70: Pull complete 37d…

开放式耳机什么品牌最好?热门开放式蓝牙耳机推荐!

如今&#xff0c;开放式耳机如雨后春笋般涌现&#xff0c;丰富的产品类型确实让不少消费者陷入了选择的困境。很多人不知道哪个牌子的耳机好用&#xff0c;不过别担心&#xff0c;我精心搜罗了一批兼具时尚外观与卓越性能的开放式耳机。作为有着多年音频设备研究经验的专业人士…

sql server 行转列及列转行

图1 图2 1.行转列 &#xff08;图1->图2&#xff09; 1.方法一 (数据库通用&#xff09;&#xff0c;使用max 加case when 函数 -- 行转列 图1->图2 SELECT name,MAX(CASE WHEN subject语文 THEN score ELSE 0 END) AS "语文",MAX(CASE WHEN subject数学 …

OpenAI GPT-o1实现方案记录与梳理

本篇文章用于记录从各处收集到的o1复现方案的推测以及介绍 目录 Journey Learning - 上海交通大学NYUMBZUAIGAIRCore IdeaKey QuestionsKey TechnologiesTrainingInference A Tutorial on LLM Reasoning: Relevant methods behind ChatGPT o1 - UCL汪军教授Core Idea先导自回归…

64页精品PPT | 汽车经销商数据应用解决方案

汽车经销商正面临前所未有的盈利能力挑战。从18年起 &#xff0c;传统燃油车汽车行业开始步入低速增长阶段 &#xff0c;卖车已经挣不到钱 &#xff0c;利润往往来自任务完成的厂家返利&#xff1b;新兴的直营模式的出现 &#xff0c;冲击了传统授权经销的方式 &#xff0c;疫情…

【JS】双指针法获得满足四数之和且不重复的四元组

思路 本题做法与上一篇三数相加的做法相似&#xff0c;同样是使用双指针法&#xff0c;区别是这里多嵌一层循坏&#xff0c;遍历 i 和 j 的值&#xff08; j i 1&#xff09;&#xff0c;思路可参考笔记&#xff1a; 【JS】双指针法获得满足三数之和且不重复的三元组https://…

Git合并多个分支中的提交内容

IDEA中使用 IEAD编辑器中使用Git IEAD编辑器中使用Git 案例一&#xff1a; 把test分支的其中提交的内容合并到main分支上。 你现在通过IDEA开发的分支是test分支&#xff0c;当你在test分支把内容都写完了并且提交内容保存到了本地的git暂存区中的时候&#xff0c;如果此时你的…

【JAVA毕业设计】基于Vue和SpringBoot的员工绩效考核系统

本文项目编号 T 021 &#xff0c;文末自助获取源码 \color{red}{T021&#xff0c;文末自助获取源码} T021&#xff0c;文末自助获取源码 目录 一、系统介绍1.1 业务分析1.2 用例分析 二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行…

【C语言刷力扣】367.有效的完全平方数

题目&#xff1a; 解题思路&#xff1a; 二分查找 时间复杂度&#xff1a; 空间复杂度&#xff1a; bool isPerfectSquare(int num) {int l 0, r 50000;while (l < r) {long long mid (l r) / 2;if (num < mid * mid) {r mid - 1;}else if (num > mid*mid) …

【番外】软件设计师中级笔记关于数据库技术更新笔记问题

提问 由于软件设计师中级笔记中第九章数据库技术基础的笔记内容太多&#xff0c;我应该分几期发布呢&#xff1f;还是一期一次性发布完成。 如果分为一期发布&#xff0c;可能需要给我多一些时间&#xff0c;由于markdown格式有所差异&#xff0c;所以我需要部分进行修改与调…