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

news2024/10/16 12:24:59

文章目录

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

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

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

相关文章

unity学习-全局光照(GI)

在全局光照&#xff08;Lighting&#xff09;界面有两个选项 Realtime Light&#xff08;实时光照&#xff09;&#xff1a;在项目中会提前计算好光照以及阴影的程序&#xff0c;当你需要调用实时全局光照的时候会将程序调用出来使用 Mixed Light&#xff08;烘焙光照&#x…

如何利用kafka实现高效数据同步?

在我们之前的文章有详细介绍过Kafka的结构、特点和处理方式。具备告诉处理能力的kafka被利用在数据同步和数据传输上&#xff0c;今天来说下kafka是怎么实现高效的数据同步和传输。 一、可靠的数据传输 1. 持久性保证&#xff1a;Kafka 将数据持久化到磁盘上&#xff0c;即使在…

汽车管理系统——主界面制作

目录 主界面需要有什么&#xff1f;然后要做什么&#xff1f;添加两个主菜单&#xff08;声明&#xff09;下一步应该干什么&#xff1f;能够跳转到文件有哪几个动作&#xff1f;动作如何声明&#xff1f;为什么用选择声明指针&#xff0c;不选择直接声明这个对象&#xff1f; …

算力基础篇:从零开始了解算力

什么是算力 算力即计算能力&#xff08;Computing Power&#xff09;&#xff0c;狭义上指对数字问题的运算能力&#xff0c;而广义上指对输入信息处理后实现结果输出的一种能力。虽然处理的内容不同&#xff0c;但处理过程的能力都可抽象为算力。比如人类大脑、手机以及各类服…

git 与 github 同步

1.配置账户 git config --global user.name "你的用户名" git config --global user.email "你的邮箱" 2.输入命令创建ssh key $ ssh-keygen -t rsa -C "邮箱" //你自己注册GitHub的邮箱 输入命令之后提示输入密码&#xff0c;回车直到出现…

C++类(3)

1.如果一个类什么成员都没有&#xff0c;简称空类 但是空类真的什么都没有吗&#xff1f; 并不是&#xff0c;任何类什么都不写&#xff0c;编译器会自动生成以下6个默认成员函数 1.构造函数 class Date { public:void Init(int year, int month, int day){_year year;_mo…

kubernetes简介及安装部署

目录 一、kubernetes简介 1、k8s的概念 2、k8s各个组件及用途 3、k8s各组件之间的调用关系 4、k8s常用名词概念 5、k8s的分层架构 二、kubernetes安装部署 1、k8s集群环境搭建 2、本地解析、swap禁用 3、安装docker 4、复制harbor仓库中的证书并启动docker 5、设定d…

基于Cesium.js的可视化大屏,效果绝对的震撼!

基于 Cesium.js 的可视化大屏确实能带来震撼的效果。Cesium.js 强大的三维渲染能力&#xff0c;能够逼真地呈现地理信息和各种数据模型。 在大屏上&#xff0c;广袤的地形地貌、精细的建筑模型以及动态的数据展示&#xff0c;仿佛将观众带入一个全新的虚拟世界。无论是用于地理…

网络分析仪——提升网络性能的关键工具

目录 什么是网络分析仪&#xff1f; 1. 实时流量监控 2. 历史数据回溯分析 3. 网络性能关键指标监测 4. 可视化界面与报告生成 总结 在当今的数字化世界&#xff0c;网络的稳定性和性能直接影响企业的运营效率。网络拥堵、延迟和丢包等问题会导致用户体验的下降&#xff…

“屏” 步青云:轻松开启录屏功能,Windows 实例教学

你知道 Windows 的录屏功能怎么打开吗&#xff1f;在当今数字化时代&#xff0c;录屏功能成为了许多人工作、学习和娱乐中的得力助手。无论是制作教学视频、记录游戏精彩瞬间&#xff0c;还是为线上会议留存资料&#xff0c;掌握录屏技能都至关重要。今天我们不仅会介绍 Window…

springboot智能网上问诊系统-计算机毕业设计源码99634

摘要 智能网上问诊系统作为医疗健康领域的重要应用之一&#xff0c;为患者提供了便捷的医疗服务途径。本研究旨在基于Springboot框架设计和实现一套智能网上问诊系统&#xff0c;结合医疗专业知识&#xff0c;实现患者与医生之间的在线咨询和诊疗服务。 通过对Springboot框架…

如何从模块内部运行 Pytest

在 Python 中&#xff0c;pytest 是一个强大的测试框架&#xff0c;用于编写和运行测试用例。通常我们会在命令行中运行 pytest&#xff0c;但是有时你可能希望从模块或脚本的内部运行 pytest&#xff0c;比如为了自动化测试或集成到某个工作流程中。 1、问题背景 当你从模块…

网络爬虫-数美滑块验证码

仅供研究学习使用。 今天带来的是数美滑块验证码的逆向 目标站 --> 传送门 解决此类验证码 首先要解决滑动距离的判定 无论是使用selenium还是使用协议的方式来破解 都绕不开滑动距离的识别 滑动距离可以参考以前我博客上的方式&#xff0c;或者找一找开源的一些算法&am…

秋招突击——8/6——万得数据面试总结

文章目录 引言正文面经整理一1、讲一下java的多态&#xff0c;重载&#xff0c;重写的概念&#xff0c;区别2、说一下Java的数组&#xff0c;链表的结构&#xff0c;优缺点3、创建java线程的方式有哪些&#xff0c;具体说说4、创建线程池呢、每个参数的意义5、通过那几种方式保…

java基础(5)继承与多态

目录 ​编辑 1.前言 2.正文 2.1继承 2.1.1继承概念 2.1.2继承语法 2.1.3子类访问父类 2.1.4super关键字 2.2多态 2.2.1多态概念 2.2.2多态条件 2.2.3重写 2.2.4向上转型与向下转型 2.2.5为什么要使用多态 3.小结 1.前言 哈喽大家好啊&#xff0c;今天继续来为大…

汽车开发流程管理工具赋能安全与质量

随着数字化、人工智能、自动化系统及物联网技术的迅速发展&#xff0c;工程驱动型企业正面临重大转型挑战&#xff0c;亟需加速并深化其变革步伐。众多企业正试图通过采用基于模型的系统工程(MBSE)、产品线工程(PLE)、ASPICE、安全、网络安全、软件定义汽车、敏捷和精益开发实践…

微信小程序上传组件封装uploadHelper2.0使用整理

一、uploadHelper2.0使用步骤说明 uploadHelper.js ---上传代码封装库 cos-wx-sdk-v5.min.js---腾讯云&#xff0c;对象存储封装库 第一步&#xff0c;下载组件代码&#xff0c;放置到自己的小程序项目中 第二步、 创建上传对象&#xff0c;执行选择图片/视频 var _this th…

【java Web如何开发?】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

讯飞与腾讯云:Android 实时语音识别服务对比选择

在 移动端 接入实时语音识别方面&#xff0c;讯飞和腾讯云都是优秀的选择&#xff0c;但各有其特点和优势。以下是对两者的详细比较&#xff1a; 一、讯飞语音识别 1.1 讯飞实时语音识别介绍 1.1.1 功能特点 1.支持多种语言识别&#xff0c;满足不同语种用户的需求。(普通话/…

Kafka之消费者组与消费者

消费者&#xff08;Consumer&#xff09;在Kafka的体系结构中是用来负责订阅Kafka中的主题&#xff08;Topic&#xff09;&#xff0c;并从订阅的主题中拉取消息后进行处理。 与其他消息中间件不同&#xff0c;Kafka引入一个逻辑概念——消费组&#xff08;Consumer Group&…