Gin+Gorm练手小项目-bubble(实践常见的 Web 开发模式和技术栈)

gin+gorm的小项目

写在开头

  • 这是一个基于Q1mi的bubble项目提供,编写后端实现清单内容的增删改查。这是一个Gin+Gorm结合的小项目,目的只是为了理清在Gin+Gorm结合下的项目结构与编写。

  • github项目提供者相关讲解视频如下,想看具体讲解可以点击链接

https://www.bilibili.com/video/BV1gJ411p7xC/?spm_id_from=333.999.0.0&vd_source=84fc27804252448ba51ef3b6abfd5d36

  • 笔者在原作者的基础上,改进了项目结构,并将编写过程中遇到的困难以及解决方法一并提供,可以给读者帮助解决困难。

项目展示效果

项目运行结果如下:

项目运行结果

然后点击删除 睡觉选项

删除事项

更新事项表示完成:

更新事项

项目编写

项目结构

笔者的项目结构(左边)VS 原项目结构(右边)

image-20230922164925035image-20230922165517040

这里可以看出,笔者在项目层次中新添了很多包,并命名也与原本项目不同,比如config包=原dao包。这是因为笔者认为更规范,更适合大众理解中的结构层次。不过,层次不重要,只是为了之后公司中更好合作,自己编写无所谓,编码能力才是最重要的。

编写过程讲解

笔者从让人利于理解的层次结构来顺序地书写过程啊~

下载前端代码

首先因为我们这项目主打的是联系gin+gorm项目编写流程,主要瞄准于后端,要想实现文章开头的想过,所以要有前端逻辑。在这个项目中,我们首先先将Q1mi的bubble项目中的statictemplate文件夹下载到我们的项目文件中,或者从笔者上传的资源【免费】Gin+Gorm练手小项目-bubble前后端代码合集资源-CSDN文库下载。因为我们不管前端,除了个别代码,我们基本实现了前后端分离的思想。

理清前后端传递数据格式
操作传递的路径方法前端传递数据正常后端返回数据
查看所有的记录localhost:9090/v1/todoGETniltodos切片
增加记录localhost:9090/v1/todoPOSTTodo结构体{“code”: 200, “msg”: “success”, “data”: todo,}
删除记录localhost:9090/v1/todo/:idDELETE路由的id{id: “delete”,}
更新记录状态localhost:9090/v1/todo/:idPUT路由的id,Todo结构体都可
编写config包

首先一个项目要想运行,必须写好配置文件,我们可以新建一个config包,然后在该包的目录下,新建文件mysql.go,意味着该文件是为了与自己的数据库mysql连接。

package config

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

//全局变量DB
var DB *gorm.DB

func InitMySql() (err error) {
    //注意这里要修改自己数据库的账户密码
	dsn := "root:wst113929@tcp(127.0.0.1:3306)/database_learn?charset=utf8mb4&parseTime=True&loc=Local"
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	return err
}

注意:InitMySql函数名要大写。函数名的大小写决定了该函数的可见性(visibility)。函数名以大写字母开头的函数是可导出的(exported),也称为公开函数(public functions),可以被其他包引用和调用。而以小写字母开头的函数是不可导出的(unexported),只能在当前包内部使用。

叙写models包

Model层:在规范的情况下,表示我们在数据库存储的数据类型,通常用struct表示。

package models

type Todo struct {
	Id     int    `json:"id"` 
	Title  string `json:"title"`
	Status bool   `json:"status"`
}

注意:属性名的大写!!!

默认情况下的id是主键,这是gorm的知识,相关内容可以在入手Gorm,小白能看懂的基础使用_法耶会输出的博客-CSDN博客内容了解。

实现dao层

dao: Data Access Object(数据访问对象)的缩写。在软件开发中,DAO 是一种设计模式,用于封装对数据源(如数据库、文件系统等)的访问和操作。

package daos

import (
	"bubble/config"
	"bubble/models"
)

//Todo 的 CURD

// CTodo 创建一个TODO记录
func CTodo(todo *models.Todo) (err error) {
	if err := config.DB.Create(&todo).Error; err != nil {
		return err
	}
	return
}

// DTodo 删除一个TODO记录 根据前端传来的路径中id删除
func DTodo(id string) (err error) {
	if err := config.DB.Where("id=?", id).Delete(&models.Todo{}).Error; err != nil {
		return err
	}
	return
}

// UTodo :更新操作,返回要更新的todo    这里是为了查找前端是否传递的正确
func UTodo(id string) (todo *models.Todo, err error) {
	if err := config.DB.Where("id=?", id).First(&todo).Error; err != nil {
		return nil, err
	}
	return
}
// STodo:储存更新的数据
func STodo(todo *models.Todo) (err error) {
	if err := config.DB.Save(&todo).Error; err != nil {
		return err
	}
	return
}

// GTodo 查询所有的记录展示
func GTodo() (todolist []*models.Todo, err error) {
	err = config.DB.Find(&todolist).Error
	return
}

总之,在这一层就是根据需求,来编写访问数据库,获取相关数据,属于gorm知识。

  • 如更新操作,这里提供了UTodoSTodo两个函数,是因为后端设计理念是:

在后端开发中,确实不能完全信任前端传递的数据,而是需要对数据进行验证和处理。这是因为前端的数据可以被篡改、伪造或以其他方式被恶意操作。

所以,必须先在UTodo中查询是否存在,然后使用STodo操作,保存数据库。会有两个函数。

完成controller层

Controller(控制器)层是负责处理来自客户端的请求,并协调其他组件(如服务、模型等)来完成请求的处理和响应。

package controllers

import (
	"bubble/daos"
	"bubble/models"
	"net/http"

	"github.com/gin-gonic/gin"
)

// 就是返回执行的函数,类似于SpringBoot+mybatis的controller层

func IndexHandler(c *gin.Context) {
	//在router包中会使用查找html所在的方式
	c.HTML(http.StatusOK, "index.html", nil)
}

// 创建记录
func CreatTodo(c *gin.Context) {
	var todo models.Todo
	//与前端传来的的数据进行绑定
	err := c.BindJSON(&todo)
	if err != nil {
		return
	}
	if err = daos.CTodo(&todo); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err,
		})
	} else {
		c.JSON(http.StatusOK, gin.H{
			"code": 200,
			"msg":  "success",
			"data": todo,
		})
	}
}

// 删除记录
func DeleteTodo(c *gin.Context) {
	//从路径中获取id的值然后实行删除
	id := c.Param("id")
	if err := daos.DTodo(id); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err,
		})
	} else {
		c.JSON(http.StatusOK, gin.H{
			//这里为什么会返回这个,要斟酌一下
			id: "delete",
		})
	}
}

// 修改记录的状态
func UpdateTodo(c *gin.Context) {
	//也是根据路径传递的id来进行筛选要更改的值
	id := c.Param("id")
	todo, err := daos.UTodo(id)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
	}
	//绑定前端传来的数据
	err = c.BindJSON(&todo)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
	}
	err = daos.STodo(todo)
	if err != nil {
		c.JSON(400, gin.H{
			"error": err.Error(),
		})
	}
}

// 得到所有数据
func GetTodo(c *gin.Context) {
	if todos, err := daos.GTodo(); err != nil {
		c.JSON(http.StatusBadRequest, err.Error())
	} else {
		c.JSON(http.StatusOK, todos)
	}
}

该层就是调用dao层的函数,然后进行返回 c *gin.Context类型的函数,以便之后函数调用

编写router层

router:路由层,改成编写每个路由执行的函数。属于gin的知识范畴。

package routers

import (
	"bubble/controllers"

	"github.com/gin-gonic/gin"
)

func SetUpRouter() *gin.Engine {
	r := gin.Default()
	//告诉gin到static找今天文件
	r.Static("/static", "static")
	//告诉gin在templates找html文件
	r.LoadHTMLGlob("template/*")

	r.GET("/", controllers.IndexHandler)

	v1Group := r.Group("/v1")
	{
		//增加数据,post请求
		v1Group.POST("/todo", controllers.CreatTodo)
		//获取数据,get请求
		v1Group.GET("todo/", controllers.GetTodo)
		//删除数据,delete请求
		v1Group.DELETE("todo/:id", controllers.DeleteTodo)
		//更新数据,put请求
		v1Group.PUT("todo/:id", controllers.UpdateTodo)
	}
	return r
}

该层如果懂gin知识,就简单~

main.go

在已经完成了其他包文件的开发,主要业务逻辑已经在其他包中实现的情况,此时main.go文件就是调用,运行。

package main

import (
	"bubble/config"
	"bubble/models"
	"bubble/routers"
)

func main() {
	err := config.InitMySql()
	if err != nil {
		//初始化连接失败
		panic(err)
	}
	//进行迁移
	err = config.DB.AutoMigrate(&models.Todo{})
	if err != nil {
		//迁移失败
		panic(err)
	}

	r := routers.SetUpRouter()
	err = r.Run(":9090")
	if err != nil {
		//运行失败
		panic(err)
	}

}

然后再terminal中输入

go run main.go

终端运行界面

然后在浏览器输入127.0.0.1:9090就可以看到自己的界面

项目运行结果

编写过程完结撒花~

出错处理

  • 界面看不到自己的记录,而数据库有?

笔者在最先运行的时候,增加删除更新等操作均可以完成,甚至在前端界面也有信息提示,表示已完成,数据库也可以看到信息,但是界面始终不显示数据库信息。like this:

错误界面

然后就返回goland看到terminal一大片爆红

terminal中报错

点击图中我出错的文件位置,看到在 err = config.DB.Find(todolist).Error我只是写了todolist,但是应该find函数参数应该是 指向切片的指针,而不是仅仅的切片。

image-20230922195422311

更改正确的是:

更改为正确代码

最后重新在terminal中运行,就可以得到正确的界面啦

  • 其他错误

在编写的过程中还碰到了其他错误,这里就不一一叙述啦,反正gin.defelut()函数默认提供例子记录功能,所以可以在终端terminal中看到自己出错的信息,根据错误修改代码。多搜,多看,多写!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go微服务开发是利用Go语言进行微服务架构的开发方式。在这个问题中,使用了gin、grpc和etcd进行重构grpc-todolist项目Gin是一个轻量级的Web框架,使用它可以快速构建高性能的Web应用程序。它具有简单易用、性能出色和灵活的特点。在微服务开发中,Gin可以作为HTTP服务器框架,处理和响应客户端的HTTP请求。 gRPC是一种高性能、开源的远程过程调用(RPC)框架。它支持多种编程语言,并使用带有协议缓冲区的Google Protocol Buffers进行数据交换。在微服务架构中,gRPC可以用于服务之间的通信,通过定义接口和消息格式,实现服务间的数据传输和调用。 Etcd是一个高可靠、分布式的键值存储系统。它使用Raft一致性算法来保证数据的可靠性和一致性。在微服务开发中,Etcd可以作为服务发现和配置管理的工具,用于注册和发现各个微服务的信息。 对于重构grpc-todolist项目来说,使用gin可以将原有的HTTP接口改写为更加高性能的接口,提高整个系统的性能。通过使用gRPC,可以将原有的接口定义为gRPC接口,实现服务间的高效通信,并且易于扩展和维护。同时,借助Etcd实现服务注册和发现,提高系统的可用性和灵活性。 总而言之,通过使用gin、grpc和etcd对grpc-todolist项目进行重构,可以提高系统性能、扩展性和可维护性。这种微服务开发方式能够更好地适应大规模分布式系统的需求,使得系统更加稳定和可靠。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值