小清单系统分析,基于gin和gorm框架

首先这是一个基于gin框架和gorm框架的简单web小项目,从前端发送各种请求,后端来处理相应的请求。请求可以简单的分为GET/POST/PUT/DELETE。GET请求,用来从后端拉取数据展示到前端。POST请求,用来从前端的POST请求中获取数据,存放到数据库中。PUT请求,用来更新数据库的数据。DELETE请求,用来删除数据库中对应的数据的。

1、先对系统进行分层设置

若是不分层,所有的代码都写在了main.go文件(注:main.go文件是程序的入口文件,该文件的逻辑应尽量少一些。)中。显得逻辑混乱,无论阅读代码还是修改代码都是一种负担。可简单的分为:

  1. controller层:

controller层为控制器层,将处理前端发送的请求的函数,放在该层。

  1. dao层:

dao层,为数据库层,放置与数据库有关的操作。例如数据库的打开、关闭、将结构体映射成数据库中的
表。

  1. models层:

models层,为模板层。用来放置模板。与模板有关的操作,放置在该层中。例如:增、删、改、查放置在models层中。

  1. routers层:

routers层为路由层,与路由有关的操作可以放置在该层中。例如创建路由引擎、加载静态文件、加载html文件、路由分组。

  1. main函数入口:在项目目录下新建一个main.go文件,这里是程序的入口。

注:各层之间的关系:URL—>>controller—>>logic(逻辑复杂的情况下需要该层)—>>models
请求来了–》控制器–》具体的业务逻辑–》模型层的增删改查

2、书写各层对应的代码

主函数对应代码:

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
}

3、总结

总的来说,代码中的注释写的很清晰,将程序分层之后,不仅阅读方便,而且更改容易。以后尽量用这种方式写代码。慢慢来,加油!!!

你可能感兴趣的:(golang,前端,json,golang)