首先这是一个基于gin框架和gorm框架的简单web小项目,从前端发送各种请求,后端来处理相应的请求。请求可以简单的分为GET/POST/PUT/DELETE。GET请求,用来从后端拉取数据展示到前端。POST请求,用来从前端的POST请求中获取数据,存放到数据库中。PUT请求,用来更新数据库的数据。DELETE请求,用来删除数据库中对应的数据的。
若是不分层,所有的代码都写在了main.go文件(注:main.go文件是程序的入口文件,该文件的逻辑应尽量少一些。)中。显得逻辑混乱,无论阅读代码还是修改代码都是一种负担。可简单的分为:
controller层为控制器层,将处理前端发送的请求的函数,放在该层。
dao层,为数据库层,放置与数据库有关的操作。例如数据库的打开、关闭、将结构体映射成数据库中的
表。
models层,为模板层。用来放置模板。与模板有关的操作,放置在该层中。例如:增、删、改、查放置在models层中。
routers层为路由层,与路由有关的操作可以放置在该层中。例如创建路由引擎、加载静态文件、加载html文件、路由分组。
注:各层之间的关系:URL—>>controller—>>logic(逻辑复杂的情况下需要该层)—>>models
请求来了–》控制器–》具体的业务逻辑–》模型层的增删改查
主函数对应代码:
package main
/*
连接数据库,然后根据用户的请求去实现增、删、改、查
*/
import (
"main.go/dao"
"main.go/routers"
)
func main() {
//创建数据库
//create database bubble; 创建一个名为bubble的数据库
//连接数据库
err := dao.InitMysql()
if err != nil {
panic(err)
}
//在执行完main函数里面所有的程序之后,执行defer语句
defer dao.Class()
//模型绑定
dao.InnitModel() //传一个空结构体指针类型,在数据库中创建对应的表。
//2、运行这个gin框架
r := routers.SetupRouter()
r.Run() //没有定义端口号,所以使用默认的端口号,8080
}
通过对程序分层,我们可以看到main主函数逻辑很清晰。先是调用InitMysql()函数,进行数据库的连接。在通过InnitModel()函数,将结构体与数据库中的表进行映射。在通过SetupRouter()函数,来管理各种路由以及对各种请求的处理,最后,运行gin框架。
1、controller层对应代码
package controller
/*
URL--->>controller--->>logic--->>models
请求来了--》控制器--》具体的业务逻辑--》模型层的增删改查
*/
import (
"github.com/gin-gonic/gin"
"main.go/models"
"net/http"
)
func IndexHeadler(c *gin.Context) { //接口名为slit
c.HTML(200, "index.html", nil) //StatusOK = 200,可以看到statusok就是常量200,200表示状态正常
}
func CreateTodo(c *gin.Context) {
//前端页面填写待办事项,点击提交,会发送请求到这里
//1、从请求中把数据拿出来
var todo models.ToDO //todo为结构体实例或者说是对象
err := c.BindJSON(&todo)
if err != nil {
return
}
//2、存入到数据库中
err = models.CreateATodo(&todo)
//这个地方是错误处理,创建完表之后,把错误信息返回。
if err != nil {
c.JSON(http.StatusOK, gin.H{ //gin.H,这个H相当于map[string]interface{},调用JSON将map类型的数据转换成为json格式并返回给前端,这个gin.H就把map[string]interface{}给代替了。
"err": err.Error(), //给前端返回一个错误,之所以返回一个200是说明请求是正常的,但是在将数据存放到数据库时,发生了错误
})
} else {
c.JSON(http.StatusOK, &todo) //把todo结构体实例转换成Json格式来进行返回,返回到前端。
}
//这样实际上是把存入数据和返回响应放在一起了。
//3、返回一个响应
}
func GetTodoList(c *gin.Context) {
//查询to_dos这个表里所有的数据
//该切片中存放的数据是todo结构体的实例或者说是对象
todolist, err := models.GetAllTodo()
if err != nil {
c.JSON(http.StatusOK, gin.H{
"error": err.Error(), //表示get请求是正常的,但是在从数据库中查询数据时发生了错误。
})
} else {
c.JSON(http.StatusOK, &todolist) //在将所有的值以Json格式数据,在前端展示。
}
}
func UpdateATodo(c *gin.Context) { //:id相当于不知道具体的id值是什么,这是一个变量用来存放id值。
id, ok := c.Params.Get("id")
if !ok {
c.JSON(http.StatusOK, gin.H{"error": "这个是无效的id"})
return
}
todo, err := models.GetATodo(id)
if err != nil {
c.JSON(http.StatusOK, gin.H{"err": err.Error()})
}
c.BindJSON(&todo) //从数据库中把数据拿出来之后,在对数据做一个更改。
if err = models.UpdateATodo(todo); err != nil { //没有对数据进行任何的更新。
c.JSON(http.StatusOK, gin.H{"err": err.Error()})
} else {
c.JSON(http.StatusOK, todo) //没有对原来的数据做任何跟新,原来的数据是多少仍然是多少。
}
}
func DeleteATodo(c *gin.Context) {
id, ok := c.Params.Get("id") //c.Params.Get()主要功能就是得到请求中的id值,并赋值给变量id。
if !ok {
c.JSON(http.StatusOK, gin.H{"err": "无效的id"})
return
}
err := models.DelectATodo(id)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"error": "这个是无效的id", //说明delete请求有效,但是在删除具体的值的时候,发生了错误。
})
} else {
c.JSON(http.StatusOK, gin.H{"id": "id对应的数据库中的数据删除成功"})
}
}
2、dao层对应代码
package dao
import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"main.go/models"
)
var (
DB *gorm.DB //定义一个全局变量,该变量为指向DB结构体的指针变量,通过该指针变量可以实现对数据库的各种操作,相当于数据库引擎。
)
func InitMysql() (err error) {
dsn := "root:123456@(127.0.0.1:3306)/bubble?charset=utf8mb4&parseTime=True&loc=Local"
DB, err = gorm.Open("mysql", dsn)
if err != nil {
panic(err)
}
return nil
}
func InnitModel() {
DB.AutoMigrate(&models.ToDO{})
}
func Class() {
DB.Close()
}
3、models层对应代码
package models
import "main.go/dao"
type ToDO struct {
Id int `json:"id"` //与前端进行数据交互的时候,要用json格式。通过tag标签,实现将结构体数据转换为前端的Json数据。
Title string `json:"title"`
Status bool `json:"status"`
}
//这是一个todo模型
//把与这张表有关的操作,放在models层中。比如增、删、改、查操作。
func CreateATodo(todo *ToDO) (err error) {
if err = dao.DB.Create(&todo).Error; err != nil {
return err
}
return nil
}
func GetAllTodo() (todolist []*ToDO, err error) {
if err = dao.DB.Find(&todolist).Error; err != nil {
return nil, err
}
return //将查询到的数据都存放到切片todolist中。
}
func GetATodo(id string) (todo *ToDO, err error) {
err = dao.DB.Where("id=?", id).First(todo).Error
if err != nil {
return nil, err
}
return
}
func UpdateATodo(todo *ToDO) (err error) {
err = dao.DB.Save(&todo).Error
return
}
func DelectATodo(id string) (err error) {
err = dao.DB.Where("id=?", id).Delete(&ToDO{}).Error
return
}
4、routers层对应代码
package routers
import (
"github.com/gin-gonic/gin"
"main.go/controller"
)
func SetupRouter() *gin.Engine {
//1、先创建一个gin框架引擎
r := gin.Default()
//告诉gin框架去哪里找index.html文件
r.LoadHTMLGlob("./templates/*")
//告诉gin框架引用的模板文件的静态文件去哪里找
r.Static("/static", "static")
//3、前段发出一个get请求,我们后端给出相应的响应
r.GET("/slit", controller.IndexHeadler) //第3个参数可以任意填,不影响最后的结果
v1Group := r.Group("v1") //首先是路由分组,路由分组完成后,在{}里面写一些增、删、改、查的操作
{
//待办事项
//添加
v1Group.POST("/todo", controller.CreateTodo)
//查看所有的待办事项
v1Group.GET("/todo", controller.GetTodoList)
//修改某一个待办事项
v1Group.PUT("/todo/:id", controller.UpdateATodo)
//删除某一个待办事项
v1Group.DELETE("/todo/:id", controller.DeleteATodo)
}
return r
}
总的来说,代码中的注释写的很清晰,将程序分层之后,不仅阅读方便,而且更改容易。以后尽量用这种方式写代码。慢慢来,加油!!!