Contents

Study Golang「2」

study golang / demo3

mysql+gorm+MVC

项目架构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
D:.
├─ go.mod
├─ go.sum
├─cmd
│  └─main
│          main.go
└─pkg
    ├─config
    │      app.go
    ├─controllers
    │      book-controller.go
    ├─models
    │      book.go
    ├─routes
    │      bookstore-routes.go
    └─utils
            utils.go

这里直接按照mvc思想去编写了,互相引用比较多,可以初始化规范一点了go mod init github.com/your_username/go-bookstore,但是我比较懒,所以就没有这样(

整体代码

1. 编写 routes/routes.go 文件

先把接口定义好,这样后面就知道要写什么了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package routes

import (
	"zsm/pkg/controllers"

	"github.com/gorilla/mux"
)

var RegisterBookStoreRoutes = func(router *mux.Router) {
	// 将 /book/ 路径和 POST 方法映射到 controllers.CreateBook 函数。
	// 也就是说,当一个 POST 请求发送到 /book/ 时,controllers.CreateBook 函数将被调用来处理这个请求。
	router.HandleFunc("/book/", controllers.CreateBook).Methods("POST")
	router.HandleFunc("/book/", controllers.GetBook).Methods("GET")
	router.HandleFunc("/book/{BookId}", controllers.GetBookById).Methods("GET")
	router.HandleFunc("/book/{BookId}", controllers.UpdateBook).Methods("PUT")
	router.HandleFunc("/book/{BookId}", controllers.DeleteBook).Methods("DELETE")
}

2. 编写 config/app.go 文件

这里提前建个库

CREATE DATABASE gotest CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package config

import (
	"fmt"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

var (
	db *gorm.DB
)

func Connect() {
	d, err := gorm.Open("mysql", "root:mnbvcxz123321.@tcp(127.0.0.1:3306)/gotest?chartset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err)
	}
	fmt.Println("Connect to database successfully")
	db = d
}

func GetDB() *gorm.DB {
	return db
}

有一说一,感觉gorm没有nodejs那边的orm好用

3. 编写 utils/utils.go 文件

这里的函数一般用来代码复用/解藕逻辑/统一格式,放的一般是常用的,小而通用的函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package utils

import (
	"encoding/json"
	"io"
	"net/http"
)

func ParseBody(r *http.Request, x interface{}) {
	if body, err := io.ReadAll(r.Body); err == nil {
		if err := json.Unmarshal([]byte(body), x); err != nil {
			return
		}
	}
}

4. 编写 models/models.go 文件

这里有个小东西,在定义结构体时,嵌套 gorm.Model 结构体,它包含了默认的字段:ID(主键)、CreatedAt、UpdatedAt 和 DeletedAt(软删除)。

在go包初始化时init函数会自动执行,所以我们可以在这个时候连接/初始化数据库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package models

import (
	"zsm/pkg/config"

	"github.com/jinzhu/gorm"
)

var db *gorm.DB

type Book struct {
    gorm.Model
	Name        string `json:"name"`
	Author      string `json:"author"`
	Publication string `json:"publication"`
}
func init() {
    config.Connect()
    db = config.GetDB()
    // 使用 GORM 的 AutoMigrate 方法自动迁移 Book 结构体。
    // 自动迁移会创建或更新数据库表,使其与 Book 结构体匹配。如果表不存在,则创建表;如果表已存在,则更新表结构以匹配 Book 结构体的定义。
    db.AutoMigrate(&Book{})
}

// CreateBook 方法用于创建 Book 结构体的实例并插入到数据库中。
func (b *Book) CreateBook() *Book{
    db.NewRecord(b)
    db.Create(&b)
    return b
}

// GetAllBooks 方法用于从数据库中获取所有 Book 结构体的实例。
func GetAllBooks() []Book {
    var Books []Book
    db.Find(&Books)
    return Books
}

// GetBookById 方法用于从数据库中获取指定 ID 的 Book 结构体的实例。
func GetBookById(Id int64) (*Book, *gorm.DB) {
    var getBook Book
    db:=db.Where("ID=?", Id).Find(&getBook)
    return &getBook, db
}

// DeleteBook 方法用于从数据库中删除指定 ID 的 Book 结构体的实例。
func DeleteBook(ID int64) Book {
    var book Book
    db.Where("ID=?", ID).Delete(book)
    return book
}

5. 编写 main.go 文件

这里其实就是最起初的启动项了,绑定端口还有创建路由即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
	"log"
	"net/http"
	"zsm/pkg/routes"

	"github.com/gorilla/mux"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	r := mux.NewRouter()
	routes.RegisterBookStoreRoutes(r)
	http.Handle("/", r)
	log.Fatal(http.ListenAndServe("localhost:9010", r))
}

编写 book-controller.go 文件

就是传统的controllers层罢了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package controllers

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"zsm/pkg/models"
	"zsm/pkg/utils"

	"github.com/gorilla/mux"
)

var NewBook models.Book

func GetBook(w http.ResponseWriter, r *http.Request) {
	newBooks := models.GetAllBooks()
	res, _ := json.Marshal(newBooks)
	w.Header().Set("Content-Type", "pkglication/json")
	w.WriteHeader(http.StatusOK)
	w.Write(res)
}

// GetBookById 根据书籍ID获取书籍详情
// 通过URL路径参数获取书籍ID,然后调用models包中的GetBookById函数获取书籍详情,
// 最后将书籍详情以JSON格式返回给客户端。
func GetBookById(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	bookId := vars["bookId"]
	ID, err := strconv.ParseInt(bookId, 0, 0)
	if err != nil {
		fmt.Println("error write parsing")
	}
	bookDetails, _ := models.GetBookById(ID)
	res, _ := json.Marshal(bookDetails)
	w.Header().Set("Content-Type", "pkglication/json")
	w.WriteHeader(http.StatusOK)
	w.Write(res)
}

// CreateBook 创建新的书籍记录
// 解析请求体中的书籍信息,然后调用models包中的CreateBook方法创建新的书籍记录,
// 最后将创建结果以JSON格式返回给客户端。
// 注意:这里假设models.Book结构体有一个CreateBook()方法来处理创建逻辑。
func CreateBook(w http.ResponseWriter, r *http.Request) {
	CreateBook := &models.Book{}
	utils.ParseBody(r, CreateBook)
	b := CreateBook.CreateBook()
	res, _ := json.Marshal(b)
	w.Header().Set("Content-Type", "pkglication/json")
	w.WriteHeader(http.StatusOK)
	w.Write(res)
}

// DeleteBook 根据书籍ID删除书籍记录
// 通过URL路径参数获取书籍ID,然后调用models包中的DeleteBook函数删除书籍记录,
// 最后将删除结果以JSON格式返回给客户端。
func DeleteBook(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	bookId := vars["bookId"]
	ID, err := strconv.ParseInt(bookId, 0, 0)
	if err != nil {
		fmt.Println("error while parsing")
	}
	book := models.DeleteBook(ID)
	res, _ := json.Marshal(book)
	w.Header().Set("Content-Type", "pkglication/json")
	w.WriteHeader(http.StatusOK)
	w.Write(res)
}

// UpdateBook 更新书籍信息
// 解析请求体中的书籍信息,然后结合URL路径参数中的书籍ID,调用models包中的相关方法更新书籍记录,
// 最后将更新后的书籍详情以JSON格式返回给客户端。
func UpdateBook(w http.ResponseWriter, r *http.Request) {
	var updateBook = &models.Book{}
	utils.ParseBody(r, updateBook)
	vars := mux.Vars(r)
	bookId := vars["bookId"]
	ID, err := strconv.ParseInt(bookId, 0, 0)
	if err != nil {
		fmt.Println("error while parsing")
	}
	bookDetails, db := models.GetBookById(ID)
	if updateBook.Name != "" {
		bookDetails.Name = updateBook.Name
	}
	if updateBook.Author != "" {
		bookDetails.Author = updateBook.Author
	}
	if updateBook.Publication != "" {
		bookDetails.Publication = updateBook.Publication
	}
	db.Save(&bookDetails)
	res, _ := json.Marshal(bookDetails)
	w.Header().Set("Content-Type", "pkglication/json")
	w.WriteHeader(http.StatusOK)
	w.Write(res)
}

study golang / demo4

mysql+gorm+MVC+gin

项目架构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
.
├─ go.mod
├─ go.sum
├─ cmd
│  └─ main
│      └─ main.go
└─ pkg
    ├─ config
    │      └─ app.go
    ├─ controllers
    │      └─ book-controller.go
    ├─ models
    │      └─ book.go
    └─ routes
       └─ bookstore-routes.go

数据库还是用上一个,比较懒导致的

整体代码

1. 编写 routes/routes.go 文件

因为所有操作都从请求接口开始,定义好路由可以帮助我们明确应用的整体结构。

在路由确定之后,我们可以进一步编写控制器和模型,这样可以确保应用的各个部分都能协调工作。

虽然每个人的开发习惯和业务逻辑可能不同,但从路由入手通常是一个推荐的方法,它能帮助你更清晰地组织代码, 并且让你曾经觉得难以完成的独立开发一个项目变得轻松可行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package routes

import (
	"github.com/gin-gonic/gin"
	"github.com/zsm/go-bookstore/pkg/controllers"
)

func Router() *gin.Engine {
	r := gin.Default()
	book := r.Group("/book")
	{
		book.GET("/", controllers.GetBookTest)
		book.GET("/:bookId", controllers.GetBookByIdTest)
		book.POST("/", controllers.CreateBookTest)
		book.PUT("/:bookId", controllers.UpdateBookTest)
		book.DELETE("/:bookId", controllers.DeleteBookTest)
	}
	return r
}

2. 编写config/app.go 文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package config

import (
	"fmt"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

var (
	db *gorm.DB
)

func Connect() {
	d, err := gorm.Open("mysql", "root:mnbvcxz123321.@tcp(127.0.0.1:3306)/gotest?chartset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err)
	}
	fmt.Println("Connect to database successfully")
	db = d
}

func GetDB() *gorm.DB {
	return db
}

3. models/models.go

这里其实和demo3差不多,这里要稍微注意一点,查询的时候要写入ID=?,这样去防止sql注入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package models

import (
	"github.com/jinzhu/gorm"
	"github.com/zsm/go-bookstore/pkg/config"
)

var db *gorm.DB

type Book struct {
	gorm.Model
	Name        string `json:"name"`
	Author      string `json:"author"`
	Publication string `json:"publication"`
}

func init() {
	config.Connect()
	db = config.GetDB()
	db.AutoMigrate(&Book{})
}

func (b *Book) CreateBook() *Book {
	db.NewRecord(b)
	db.Create(&b)
	return b
}

func GetAllBooks() []Book {
	var Books []Book
	db.Find(&Books)
	return Books
}

func GetBookById(Id int64) (*Book, *gorm.DB) {
	var getBook Book
	db := db.Where("ID=?", Id).Find(&getBook)
	return &getBook, db
}

func DeleteBook(ID int64) Book {
	var book Book
	db.Where("ID=?", ID).Delete(&book)
	return book
}

4. 编写 mian.go 文件

这里就很简洁了,两行搞定bro

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import (
	"fmt"

	"github.com/zsm/go-bookstore/pkg/routes"
)

func main() {
	r := routes.Router()
	fmt.Println("Server is running on localhost:9010")
	r.Run(":9010")
}

5. 编写 controllers/controller.go 文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package controllers

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/zsm/go-bookstore/pkg/models"
)

// NewBook 是一个用来创建新书的结构体
var NewBook models.Book

// GetBookTest 函数返回所有的书籍
func GetBookTest(c *gin.Context) {
	newBooks := models.GetAllBooks()
	c.JSON(http.StatusOK, newBooks)
}

// GetBookByIdTest 函数返回指定ID的书籍
func GetBookByIdTest(c *gin.Context) {
	bookId := c.Param("bookId")
	ID, _ := strconv.ParseInt(bookId, 0, 0)
	bookDetails, _ := models.GetBookById(ID)
	// 以JSON格式返回书籍详情
	c.JSON(http.StatusOK, bookDetails)
}

// CreateBookTest 函数创建一个新的书籍并返回详细信息
func CreateBookTest(c *gin.Context) {
	// 初始化一个新书籍结构体
	var CreateBook = &models.Book{}
	// 从请求体中解析书籍信息
	c.ShouldBindJSON(CreateBook)
	// 创建书籍并保存到数据库
	b := CreateBook.CreateBook()
	// 以JSON格式返回书籍详情
	c.JSON(http.StatusOK, b)
}

func DeleteBookTest(c *gin.Context) {
	bookId := c.Param("bookId")
	ID, err := strconv.ParseInt(bookId, 0, 0)
	if err != nil {
		fmt.Printf("解析错误!")
	}
	// 删除之前获取书籍详情, 并删除书籍
	book, _ := models.GetBookById(ID)
	models.DeleteBook(ID)
	c.JSON(http.StatusOK, book)
}

func UpdateBookTest(c *gin.Context) {
	var updateBook = &models.Book{}
	c.ShouldBindJSON(updateBook)
	bookId := c.Param("bookId")
	ID, err := strconv.ParseInt(bookId, 0, 0)
	if err != nil {
		fmt.Println("解析错误!")
	}
	bookDetails, db := models.GetBookById(ID)
	// 如果Name、Author、Publication字段有更新,则更新数据库对应数据
	if updateBook.Name != "" {
		bookDetails.Name = updateBook.Name
	}
	if updateBook.Author != "" {
		bookDetails.Author = updateBook.Author
	}
	if updateBook.Publication != "" {
		bookDetails.Publication = updateBook.Publication
	}
	// 保存更新后的书籍信息到数据库
	db.Save(&bookDetails)
	c.JSON(http.StatusOK, bookDetails)
}