GO语言工程实践课后作业| 青训营

GO语言工程实践课后作业| 青训营

依赖管理

Go依赖管理演进

早期版本的Go语言并没有官方的依赖管理工具。开发者通常将项目的依赖包放在项目目录下的vendor文件夹中,这样每个项目都会复制一份自己的依赖包。这种方式存在一些问题,例如依赖包更新困难、冲突处理复杂等。

为了解决这些问题,社区推出了一些第三方的依赖管理工具,例如Godepglide。这些工具通过管理项目的vendor文件夹,实现对依赖包版本的记录和管理。开发者可以使用类似于Godep saveglide install的命令,将当前项目所需的所有依赖包复制到vendor文件夹中,并生成一个描述依赖关系的文件。

然而,这些工具仍然存在一些问题。首先,它们无法解决全局依赖冲突的问题,即当多个项目依赖相同的包但版本不同时可能导致的冲突。其次,它们的配置文件格式各不相同,缺乏统一性和标准化。

为了解决这些问题,Go官方在Go 1.11版本中引入了Go Modules作为官方的依赖管理解决方案。Go Modules通过在项目目录中添加一个go.mod文件来管理项目的依赖关系。在go.mod文件中,开发者可以明确指定所需的依赖包及其版本要求。

使用Go Modules的好处是,它能够自动解决全局依赖冲突问题,避免了手动处理依赖包版本的繁琐工作。此外,Go Modules还支持从代理服务器下载依赖包,提高了依赖包的下载速度。

随着时间的推移,Go Modules不断发展和改进。它增加了对私有仓库、替代模块和版本补丁的支持,提供了更多灵活且强大的功能。同时,一些第三方工具如depvgo也逐渐被集成到Go Modules中。

总结而言,Go语言的依赖管理在过去几年里经历了一次重要的演进。从早期的手动复制依赖包到第三方工具的出现再到官方的Go Modules,Go的依赖管理水平得到了显著提高。Go Modules提供了一个统一、标准化的依赖管理解决方案,并不断更新和改进,为Go开发者提供了更好的依赖管理体验。

Go Module实践

Go Module 是 Go 语言中用于管理包依赖关系的工具。它自 Go 1.11 版本开始成为标准的包管理方式,取代了原有的 GOPATH 和 vendor 目录的方式。Go Module 的出现,极大地简化了 Go 项目的包管理和版本控制,并提供了更好的依赖管理和构建可重复性的功能。

Go Module 的基本概念是将每个项目作为一个模块(module),并有独立的模块路径。每个模块都可以包含多个包(package),并且每个模块都有一个 go.mod 文件来记录包依赖关系。

使用 Go Module 需要满足以下条件:

  1. Go 1.11 及以上版本。

    1. 需要将环境变量 GO111MODULE 设置为 on,这样才能启用 Go Module 功能。set GO111MODULE =on set GOPROXY=https://goproxy.io(win环境下的国内代理)

在一个项目中启用 Go Module 后,可以通过以下步骤进行包依赖管理:

  1. 在项目根目录下执行 go mod init 模块路径 命令来初始化一个新的模块。例如:go mod init myapp 将会创建一个名为 myapp 的模块,并生成一个默认的 go.mod 文件。
  2. 执行 go mod tidy 命令来自动分析代码中的依赖关系,并更新当前模块的 go.mod 文件。这个命令会根据代码中的实际引用,自动添加和删除模块的依赖关系。
  3. 执行 go mod vendor 命令来将所有依赖项复制到项目的 vendor 目录中。这样可以确保项目的构建过程中使用的是 vendor 目录中的依赖项,而不是全局的 GOPATH 下的依赖项。

在使用 Go Module 进行包依赖管理时,还有一些其他常用的命令和功能:

  • go mod init: 初始化一个新的模块。
  • go get: 下载指定模块的源代码,并添加到当前模块的依赖关系。
  • go list: 列出当前模块的所有依赖项。
  • go build / go run: 构建或运行包含 Go Module 的项目。
  • go mod edit: 编辑 go.mod 文件,手动添加、删除或更新依赖项。
  • go mod graph: 显示模块之间的依赖关系图。
  • go mod verify: 验证模块的依赖项已经完整下载并且没有被篡改。

Go Module 提供了更灵活和可靠的包管理方式,可以帮助开发者更好地管理项目的依赖关系,并且能够确保每个项目在不同环境中的构建结果一致。它的出现使得 Go 语言的包管理更加现代化和便捷化,也受到了开发者的广泛欢迎和推崇。

测试

单元测试

Go语言是一种强大且受欢迎的编程语言,它支持测试驱动开发(TDD)的开发方法。在Go中,编写单元测试是非常简单和直观的。在本文中,我将向您介绍如何使用Go语言编写高质量的单元测试。

首先,让我们了解一下Go语言的测试工具。Go语言内置了一个名为testing的测试框架,它提供了一些函数和工具来编写和执行测试。测试文件的名称应以_test.go结尾,这样Go编译器才会将其识别为测试文件。

接下来,让我们来看一个简单的示例。假设我们要编写一个用于向数据库插入用户函数AddUser,以下是一个包含该函数及其对应测试的示例:

go 复制代码
// user_test.go
package module
​
import (
    "fmt"
    "testing"
)
​
func TestAddUser(t *testing.T) {
fmt.Println("测试用户:")
user:=&User{}
user.AddUser()
}
​
go 复制代码
// user.go
package module
​
import (
    "fmt"
    "project/web/utils"
)
​
type User struct {
    id int
    username int
    password string
}
​
func(user *User) AddUser() error{
    sqlStr:="insert into `table`(user,password) values(?,?) "
    //执行
    _,err:=utils.Db.Exec(sqlStr,2,"ddddd")
    if(err!=nil) {
        fmt.Println("执行异常:",err)
        return err
    }
    return nil
}
​

在上面的示例中,我们首先编写了一个用于计算两个整数之和的Add函数。然后,在user_test.go文件中,我们编写了一个名为TestAddUser的测试函数。

接下来,我们需要执行测试。可以通过在终端中运行以下命令来执行所有的单元测试:

bash 复制代码
go test

Go会自动找到所有以_test.go结尾的测试文件,并执行其中的测试函数。测试结果将显示在终端上,如果所有测试通过,将会显示一个通过的消息。

除了基本的测试功能,Go语言还提供了一些辅助函数和工具,帮助我们编写更复杂、全面的单元测试。例如,我们可以使用testing包中的Skip函数来跳过特定的测试,还可以使用testing包中的Table Driven Tests功能来简化多个输入/输出测试的编写。

总结起来,Go语言的单元测试非常简单直观,而且内置的测试框架提供了丰富的功能和工具。通过编写有效的单元测试,我们可以确保代码的正确性,提高代码质量,并减少在后续开发和维护过程中可能出现的问题。希望这篇文章能够帮助您入门Go语言的单元测试。祝您编写出更健壮的代码!

Gin

Gin框架导包"github.com/gin-gonic/gin"

Gin的使用

自定义拦截器

go 复制代码
// 自定义拦截器
func myHandler() gin.HandlerFunc {
    return func(context *gin.Context) {
        context.Set("usersession", "userid-1")
        context.Next()
        context.Abort()
    }
}
​
    //传参方式url?userid=xxx&password=xxxxx  加了中间件
    //myhandler()没有指定则全部一起用
    ginServer.GET("/user/info", myHandler(), func(context *gin.Context) {
​
        //取出中间件值
        usersession := context.MustGet("usersession").(string)
        log.Println("--------------", usersession)
​
        userid := context.Query("userid")
        password := context.Query("password")
        context.JSON(http.StatusOK, gin.H{"userid": userid,
            "password": password})
    })

设置图标

导包"github.com/thinkerou/favicon"

代码ginServer.Use(favicon.New("./u=G.jpg"))

Restful Api

javascript 复制代码
//gin restful API
    ginServer.PUT("htllo", func(context *gin.Context) {
        context.JSON(200, gin.H{"msg": "hello,world"})
    })
    ginServer.DELETE("/htllo")

页面加载响应

go 复制代码
//加载静态页面
    ginServer.LoadHTMLGlob("templates/*")
​
    //响应一个页面给前端
    ginServer.GET("/index", func(context *gin.Context) {
        context.HTML(http.StatusOK, "index.html", gin.H{"msg": "hello,world"})
    })

传参方式url?userid=xxx&password=xxxxx

go 复制代码
//传参方式url?userid=xxx&password=xxxxx
    ginServer.GET("/user/info", func(context *gin.Context) {
        userid := context.Query("userid")
        password := context.Query("password")
        context.JSON(http.StatusOK, gin.H{"userid": userid,
            "password": password})
    })

传参方式user/info/1/kuangshen

go 复制代码
    //user/info/1/kuangshen
    ginServer.GET("/user/info/:userid/:username", func(context *gin.Context) {
        userid := context.Param("userid")
        username := context.Param("username")
        context.JSON(http.StatusOK, gin.H{"userid": userid, "username": username})
    })

前端给后端传json

go 复制代码
//前端给后端传json
    ginServer.POST("/json", func(context *gin.Context) {
        data, _ := context.GetRawData()
        var m map[string]interface{}
        _ = json.Unmarshal(data, &m)
        context.JSON(http.StatusOK, m)
    })

接收form表单

go 复制代码
    //接收form表单
    ginServer.POST("/sdsd", func(context *gin.Context) {
        username := context.PostForm("username")
        password := context.PostForm("password")
        context.JSON(http.StatusOK, gin.H{"username": username, "password": password})
    })
​

路由

go 复制代码
    //路由
    ginServer.GET("test", func(context *gin.Context) {
        context.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
    })
​

自定义404页面

go 复制代码
//404
    ginServer.NoRoute(func(context *gin.Context) {
        context.HTML(http.StatusNotFound, "404.html", nil)
    })

设置路由组

go 复制代码
    //路由组
    userGroup := ginServer.Group("/user")
    {
        userGroup.GET("/add")
        userGroup.POST("/login")
        userGroup.POST("/logout")
    }

启动

ginServer.Run(":8080")

整合Gin

导包

"github.com/gin-gonic/gin"

创建一个router的包

go 复制代码
func SetupRouter() *gin.Engine {
   router := gin.Default()
   return router
}

启动类下

css 复制代码
router := router.SetupRouter()
    // 启动服务器
    router.Run(":8080")

实战

需求分析

  • 支持发布帖子
  • 生成ID不重复、唯一性
  • 更新索引

1.创建启动类

erlang 复制代码
package main
​
import (
    _ "github.com/Moonlight-Zhao/go-project-example/cotroller"
    "github.com/Moonlight-Zhao/go-project-example/repository"
    "github.com/Moonlight-Zhao/go-project-example/router"
    "github.com/Moonlight-Zhao/go-project-example/utils"
)
​
func main() {
    utils.InitConfig()
    utils.InitMysql()
    repository.Init()
    router := router.SetupRouter()
    // 启动服务器
    router.Run(":8080")
​
}

2. 创建router包和类

go 复制代码
package router
​
import (
    "github.com/Moonlight-Zhao/go-project-example/service"
    "github.com/gin-gonic/gin"
)
​
func SetupRouter() *gin.Engine {
    router := gin.Default()
    // 定义路由和处理函数
    router.POST("/topic", service.Insert)
    return router
}
​

3.在config包下创建yml文件

ini 复制代码
mysql:
  url: user:password@(localhost:port)/data?charset=utf8mb4&parseTime=True&loc=Local

4.在utils包下创建初始化类

go 复制代码
package utils
​
import (
    "fmt"
    "github.com/spf13/viper"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
    "log"
    "os"
    "time"
)
​
func InitConfig() {
​
    //设置配置文件的名称为 "app"
    viper.SetConfigName("app")
​
    //添加配置文件的路径为 "config"
    viper.AddConfigPath("config")
​
    //读取配置文件并将其加载到 viper 中。
    err := viper.ReadInConfig()
    if err != nil {
        fmt.Println(err)
    }
}
​
var DB *gorm.DB
​
func InitMysql() {
    //自定义日志模板打印sql语句
    newLogger := logger.New(
        log.New(os.Stdout, "\r\n", log.LstdFlags),
        logger.Config{
            SlowThreshold: time.Second,
            LogLevel:      logger.Info,
            Colorful:      true,
        },
    )
    DB, _ = gorm.Open(mysql.Open(viper.GetString("mysql.url")), &gorm.Config{Logger: newLogger})
}
​

5.repository

go 复制代码
package repository
​
import (
    "github.com/Moonlight-Zhao/go-project-example/utils"
    "log"
    "sync"
)
​
type Topic struct {
    Id         int64  `json:"id"`
    Title      string `json:"title"`
    Content    string `json:"content"`
    CreateTime int64  `json:"create_time"`
}
type TopicDao struct {
}
​
var (
    topicDao  *TopicDao
    topicOnce sync.Once
)
​
func (Topic) TableName() string {
    return "topic"
}
​
func NewTopicDaoInstance() *TopicDao {
    topicOnce.Do(
        func() {
            topicDao = &TopicDao{}
        })
    return topicDao
}
​
func (*TopicDao) QueryTopicById(id int64) *Topic {
    return topicIndexMap[id]
}
​
func (*TopicDao) Insert(topic Topic) error {
    result := utils.DB.Create(&topic)
    if result.Error != nil {
        // 插入失败,处理错误情况
        log.Println("插入失败:", result.Error)
        return result.Error
    }
    Init()
    return nil
}
​
​
func (*TopicDao) findAllTopics() ([]Topic, error) {
    var topics []Topic
    result := utils.DB.Find(&topics)
    if result.Error != nil {
        return nil, result.Error
    }
    return topics, nil
}
​

6.索引初始化

go 复制代码
package repository
​
var (
    topicIndexMap map[int64]*Topic
    postIndexMap  map[int64][]*Post
)
​
func Init() error {
    if err := initTopicIndexNewMap(); err != nil {
        return err
    }
​
    return nil
}
​
​
// 从数据库中读取表来初始化索引
func initTopicIndexNewMap() error {
    rows, err := topicDao.findAllTopics()
    if err != nil {
        return err
    }
​
    topicTmpMap := make(map[int64]*Topic)
​
    for _, topic := range rows {
        topicTmpMap[topic.Id] = &topic
    }
​
    // 将结果存储到全局变量 topicIndexMap 中
    topicIndexMap = topicTmpMap
    return nil
}
​

7.service层

go 复制代码
package service
​
import (
    "github.com/Moonlight-Zhao/go-project-example/repository"
    "github.com/gin-gonic/gin"
    "net/http"
    "time"
)
​
func Insert(context *gin.Context) {
    // 接收form表单数据
    title := context.PostForm("title")
    content := context.PostForm("content")
​
    currentTime := time.Now()
    createTime := currentTime.Unix()
​
    u1 := repository.Topic{
        Id:         0,
        Title:      title,
        Content:    content,
        CreateTime: createTime,
    }
    topicDao := repository.NewTopicDaoInstance()
    err := topicDao.Insert(u1)
    //  repository.
    if err != nil {
        repository.Init()
        context.JSON(http.StatusBadRequest, gin.H{"msg": "发布失败"})
    } else {
        repository.Init()
        context.JSON(http.StatusOK, gin.H{"msg": "发布成功"})
    }
}
相关推荐
CallBack8 个月前
Typora+PicGo+阿里云OSS搭建个人图床,纵享丝滑!
前端·青训营笔记
Taonce1 年前
站在Android开发者的角度认识MQTT - 源码篇
android·青训营笔记
AB_IN1 年前
打开抖音会发生什么 | 青训营
青训营笔记
monster1231 年前
结营感受(go) | 青训营
青训营笔记
翼同学1 年前
实践记录:使用Bcrypt进行密码安全性保护和验证 | 青训营
青训营笔记
hu1hu_1 年前
Git 的正确使用姿势与最佳实践(1) | 青训营
青训营笔记
星曈1 年前
详解前端框架中的设计模式 | 青训营
青训营笔记
tuxiaobei1 年前
文件上传漏洞 Upload-lab 实践(中)| 青训营
青训营笔记
yibao1 年前
高质量编程与性能调优实战 | 青训营
青训营笔记
小金先生SG1 年前
阿里云对象存储OSS使用| 青训营
青训营笔记