Golang 小项目(3)


前言

本项目适合 Golang 初学者,通过简单的项目实践来加深对 Golang 的基本语法和 Web 开发的理解。
项目文件已上传至 https://github.com/Done-0/go-bookstore

项目结构

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

项目流程图

  1. 技术栈

技术栈

  1. 项目结构

项目结构图

  1. 项目路由

项目路由图

项目初始化

  1. 初始化项目文件夹
mkdir go-bookstore
cd go-bookstore
  1. 初始化 mod 文件
cd go-bookstore
go mod init github.com/your_username/go-bookstore

注意,此处的 your_username 请替换为你的 GitHub 用户名

  1. 安装依赖包
# GORM 是一个用于 Go 的 ORM(对象关系映射)库,可以简化与数据库的交互。
go get "github.com/jinzhu/gorm"
# 这个包提供了与 MySQL 数据库的连接和操作功能,使得可以使用 GORM 进行 MySQL 数据库的 CRUD 操作。
go get "github.com/jinzhu/gorm/dialects/mysql"
# Gorilla Mux 是一个强大的 HTTP 路由和调度库,可以处理请求路由,支持路径变量、查询参数等功能。
go get "github.com/gorilla/mux"

# 打开 VSCode 编辑器
code .
  1. 创建 go-bookstore 数据库

打开 Mysql 客户端,输入以下命令创建 go-bookstore 数据库:

CREATE DATABASE `database_name` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

其中,database_name 请替换为你自己喜欢的数据库名称。

  1. 初始化项目结构

一行代码在项目根目录下创建目录和空文件

# windows系统
mkdir cmd\main pkg\config pkg\controllers pkg\models pkg\routes pkg\utils & type nul > cmd\main\main.go & type nul > pkg\config\app.go & type nul > pkg\controllers\book-controller.go & type nul > pkg\models\book.go & type nul > pkg\routes\bookstore-routes.go & type nul > pkg\utils\utils.go

# Linux/mac系统
mkdir -p cmd/main pkg/config pkg/controllers pkg/models pkg/routes pkg/utils && touch cmd/main/main.go pkg/config/app.go pkg/controllers/book-controller.go pkg/models/book.go pkg/routes/bookstore-routes.go pkg/utils/utils.go

项目编写

  1. 编写 pkg/routes/bookstore-routes.go 文件
package routes

import (
    // 引入路由库 Gorilla Mux
    "github.com/gorilla/mux"
    // 引入 controllers 包: 定义控制器代码的包,比如处理 HTTP 请求
    "github.com/yourname/go-bookstore/pkg/controllers"

    // 声明变量RegisterBookStoreRoutes,这种方式允许你将函数赋值给变量,然后通过这个变量来调用函数。
    // func() 是一个匿名函数(或称为闭包),它接受一个参数 router,这个参数是 *mux.Router 类型。
    // mux.Router 是来自 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")
    }
)

注意,此处的 yourusername 请替换为你的 GitHub 用户名。

  1. 编写 pkg/config/app.go 文件
package config

import (
    "fmt"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

var (
    db * gorm.DB
)

func Connect() {
    // 其中root对应你的数据库用户名,yourpassword对应你的数据库密码,database_name对应你的数据库名称。
    d, err := gorm.Open("mysql", "root:your_password@tcp(127.0.0.1:3306)/database_name?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
}

注意,此处的 your_password 请替换为你的数据库密码。

  1. 编写 pkg/utils/utils.go 文件
package utils

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

// ParseBody 从 HTTP 请求中读取 JSON 数据并解析到指定的接口中
func ParseBody(r *http.Request, x interface{}) {
    // 读取请求体
    if body, err := io.ReadAll(r.Body); err == nil {
        // 解析 JSON 数据
        if err := json.Unmarshal([]byte(body), x); err != nil {
          return 
        }
    }
}
  1. 编写 pkg/models/models.go 文件
package models

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

var db *gorm.DB

type Book struct {
    // 嵌套 gorm.Model 结构体,它包含了默认的字段:ID(主键)、CreatedAt、UpdatedAt 和 DeletedAt(软删除)。
    gorm.Model
    Name string `json:"name"`
    Author string `json:"author"`
    Publication string `json:"publication"`
}

// init 函数是 Go 语言中的特殊函数,它会在包初始化时自动执行。通常用来做一些初始化操作,如设置数据库连接、初始化配置等。
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
}
  1. 编写 cmd/main/main.go 文件
package main

import (
    "log"
    "net/http"
    "github.com/gorilla/mux"
    _ "github.com/jinzhu/gorm/dialects/mysql"
    "github.com/Done-0/go-bookstore/pkg/routes"
)

func main() {
    // 创建一个新的路由器实例。
    r := mux.NewRouter()
    // 注册路由规则到路由器实例中。
    routes.RegisterBookStoreRoutes(r)
    //  设置路由器处理所有根路径的 HTTP 请求。
    http.Handle("/", r)
    // 启动 HTTP 服务器,并监听 localhost:9010 地址上的请求。若启动失败,记录错误信息并终止程序。
    log.Fatal(http.ListenAndServe("localhost:9010", r))
}
  1. 编写 pkg/controllers/book-controller.go 文件
package controllers

import (
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "net/http"
    "strconv"
    "github.com/Done-0/go-bookstore/pkg/utils"
    "github.com/Done-0/go-bookstore/pkg/models"
)

var NewBook models.Book

func GetBook(w http.ResponseWriter, r *http.Request) {
    newBooks := models.GetAllBooks()
    //  将newBooks 数据结构编码为 JSON 格式。json.Marshal 函数返回两个值,第一个是 JSON 编码后的字节切片 res,第二个是错误信息(如果有的话)。
    res, _ := json.Marshal(newBooks)
    // 设置响应头 Content-Type 为 application/json,告知客户端响应内容是 JSON 格式。
    w.Header().Set("Content-Type", "pkglication/json")
    // 设置 HTTP 响应的状态码为 200(OK),表示请求成功且响应内容正常。
    w.WriteHeader(http.StatusOK)
    // 将 JSON 编码后的数据 res 写入响应体,发送给客户端。
    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.Printf("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)
}

运行项目

  1. 编译项目
go run cmd/main/main.go
  1. postman 测试

进入Postman测试接口,验证项目功能是否正常:
GET ALL 接口地址:http://localhost:9010/book/
GET BY ID 接口地址:http://localhost:9010/book/1
CREATE 接口地址:http://localhost:9010/book/
UPDATE 接口地址:http://localhost:9010/book/1
DELETE 接口地址:http://localhost:9010/book/1

书籍创建之 JSON 模板:

{
    "ID": 1,
    "createdAt": "2024-08-29T13:26:00Z",
    "updatedAt": "2024-08-29T13:26:00Z",
    "DeleteAt": null,
    "name": "Go 语言",
    "author": "老子",
    "publication": "人民邮电出版社",
}

postman测试