Go + Echo + Gorm项目从0到1搭建

2024-08-15 15:17:33 浏览数 (2)

1. 前言说明

Echo 项目是一个功能强大且用途广泛的 Web 框架,用于使用 Go 编程语言构建可扩展且高性能的 Web 应用程序。它遵循简单性、灵活性和性能的原则,为开发人员提供了一个高效的工具包,用于构建强大的 Web 应用程序。

资源 系列教程

polarisxu.studygolang.com/posts/go/ec…

Bind 是 Echo 框架提供的一个方法,用于将请求中的数据绑定到指定的结构体实例上。它支持多种数据格式,包括 JSON、XML、表单数据等。

以下是 Bind 方法的一些关键点:

  1. JSON 数据:如果请求的 Content-Type 是 application/jsonBind 会尝试将请求体中的 JSON 数据解析并绑定到指定的结构体实例上。
  2. 表单数据:如果请求的 Content-Type 是 application/x-www-form-urlencodedmultipart/form-dataBind 会尝试将请求中的表单数据解析并绑定到指定的结构体实例上。
  3. 查询参数Bind 还可以将查询参数绑定到结构体实例上,但需要结构体字段带有 form 标签,例如 form:"name"
  4. 路径参数Bind 也可以将路径参数绑定到结构体实例上,但需要结构体字段带有 param 标签,例如 param:"id"

2. 安装Echo 框架

初始化项目:

创建一个项目文件并进去

代码语言:javascript复制
$ mkdir myapp && cd myapp

这将会在你项目的根目录中创建一个go.mod 文件

代码语言:javascript复制
$ go mod init myapp

接着安装Echo

代码语言:javascript复制
$go get -u github.com/labstack/echo/v4

3. 编写main文件,启动服务

创建一个main文件

代码语言:javascript复制
package main

import (
  "net/http"

  "github.com/labstack/echo/v4"
)

func main() {
  e := echo.New()
  e.GET("/", func(c echo.Context) error {
    return c.String(http.StatusOK, "Hello, World!")
  })
  e.Logger.Fatal(e.Start(":1323"))
}

在终端上,进入到正确目录中 输入

代码语言:javascript复制
$ go run main.go

看到这个就说明成功启动了, 然后访问http://localhost:1323/,就会看到映入眼帘的Hello, World! 这几个字眼.

上方文件的代码, 我们等下去解释.

4. 安装air, 热重启

Air 是一个为 Go 项目提供自动重载功能的工具。通过监听项目中文件的变化,自动重新编译并运行你的应用,从而达到热重载的效果,极大地提升开发效率。

安装下载:

代码语言:javascript复制
go get github.com/air-verse/air@latest

安装好之后, 在项目根目录下创建一个和.air.toml 的配置文件(如果项目中没有这个文件,Air 会使用默认配置)来监听文件变化。一旦检测到文件更改,Air 会自动编译并重启你的应用。

配置内容如下: (可参考)

代码语言:javascript复制
# .air.toml

# 设置项目根目录
root = "."

# [build] 部分用于定义编译命令和其他编译选项
[build]
  cmd = "go build -o main.exe ."  # Windows 上生成 .exe 文件
  bin = "main.exe"  # 指定生成的 .exe 文件名称
  include = ["*.go", "config/**/*.yaml"]  # 监视的文件变化
  exclude = ["vendor/**", "node_modules/**"]  # 不监视的文件或目录

# [run] 部分用于定义运行命令和环境变量
[run]
  cmd = "./main.exe"  # 指定运行的可执行文件
  env = ["PORT=8080"]  # 设置运行时的环境变量

# [watch] 部分用于定义监视的文件和目录
[watch]
  include = ["*.go"]
  exclude = ["vendor/**", "node_modules/**"]

# [logger] 部分用于定义日志级别
[logger]
  level = "info"

打开终端, 在项目的根目录中运行

代码语言:javascript复制
air

这就表示项目成功启动了.

5. 安装Gorm 和 mysql驱动

gorm : go中较流行的ORM库.

以下是官网描述的特性:

  • 全功能 ORM
  • 关联 (拥有一个,拥有多个,属于,多对多,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 Preload、Joins 的预加载
  • 事务,嵌套事务,Save Point,Rollback To to Saved Point
  • Context、预编译模式、DryRun 模式
  • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
  • SQL 构建器,Upsert,锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • 复合主键,索引,约束
  • 自动迁移
  • 自定义 Logger
  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  • 每个特性都经过了测试的重重考验
  • 开发者友好

安装:

代码语言:javascript复制
go get -u gorm.io/gorm

接着去下载安装mysql驱动

代码语言:javascript复制
go get gorm.io/driver/mysql

6. 连接mysql

我们将连接mysql 的 DSN 存入在.env 环境变量当中.

DSN(DataSourceName)是用于标识和定位数据库的字符串,包含数据库类型、主机、端口等信息。它通过驱动程序解析,建立与数据库的连接,提供了一种标准化的连接方式,增强了应用程序的灵活性和可移植性。

代码语言:javascript复制
DSN=用户名:密码@tcp(127.0.0.1:3306)/数据库

然后在main文件中,进行加载环境变量, 读取环境变量, grom mysql连接 等等

代码语言:javascript复制
func main() {
  // 加载环境变量
  loadEnv()
  // 初始化数据库连接
  initDataBases()
  // 启动 HTTP 服务器
  startServer()
}

加载环境变量

代码语言:javascript复制
func loadEnv() {
  env := os.Getenv("ENV")
  fmt.Println("环境变量", env)
  if env != "production" { // 生产环境是用docker运行的,会用--env-file参数指定.env文件,不需要手动加载
    err := godotenv.Load()
    if err != nil {
      log.Fatalf("Error loading .env file: %v", err)
    }
  }
}

初始化数据库连接

代码语言:javascript复制
func initDataBases() {
  // 获取.env的DSN变量
  dsn := os.Getenv("DSN")
  fmt.Println("dsn", dsn)
  // 调用 Open 方法,传入驱动名和连接字符串
  var err error
  db.DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
  // 检查是否有错误
  if err != nil {
    fmt.Println("连接数据库失败:", err)
    return
  }
  // 打印成功信息
  fmt.Println("连接数据库成功", db.DB)
}

启动服务器

代码语言:javascript复制
func startServer() {
  // 初始化 Echo 实例
  e := echo.New()
  // 启动服务器
  e.Logger.Fatal(e.Start(":8080"))
}

看到这个就说明数据库成功连接了.

接下来,我们继续完善我们的项目的目录搭建工作

7. 采用MVC的风格整理目录

learnku.com/go/t/48112

代码语言:javascript复制
my-go-project/
|-- cmd/                      # 应用程序的启动点
|   |-- main.go               # 主入口文件
|-- config/                   # 配置文件
|   |-- database.go           # 数据库配置
|   |-- server.go             # 服务器配置
|   |-- env.example           # 环境变量示例文件
|-- db/                       # 数据库相关文件
|   |-- migrations/           # 数据库迁移文件
|   |   |-- 20230801_create_users_table.go
|   |   |-- 20230802_add_roles_to_users_table.go
|-- |models/               # 数据库模型
|   |   |-- user.go           # 用户模型
|   |   |-- role.go           # 角色模型
|   |repositories/         # 数据访问层 (DAL)
|   |   |-- user.go           # 用户数据访问层
|   |   |-- role.go           # 角色数据访问层
|-- handlers/                 # HTTP 请求处理器
|   |-- user.go               # 用户请求处理器
|   |-- role.go               # 角色请求处理器
|-- middleware/               # 中间件
|   |-- auth.go               # 认证中间件
|   |-- logging.go            # 日志记录中间件
|-- routes/                   # 路由配置
|   |-- user.go               # 用户路由
|   |-- role.go               # 角色路由
|-- services/                 # 业务逻辑层
|   |-- user.go               # 用户服务
|   |-- role.go               # 角色服务
|-- utils/                    # 辅助函数和工具
|   |-- jwt.go                # JWT 工具
|   |-- validators.go         # 验证工具
|-- internal/                 # 内部模块或包
|   |-- ...
|-- static/                   # 静态文件
|-- templates/                # 模板文件
|-- go.mod                    # Go 模块元数据
|-- go.sum                    # Go 模块校验和
|-- .gitignore                # Git 忽略文件列表
|-- .env                      # 环境变量文件
|-- .dockerignore             # Docker 忽略文件列表
|-- Dockerfile                # Docker 镜像构建文件
|-- Makefile                  # 自动化任务管理文件
|-- README.md                 # 项目说明文件
|-- LICENSE                   # 许可证文件

我们先这样分好目录. 方便我们之后的代码开发.

  • model: 包含数据模型和它们的相关操作。这些模型通常对应于数据库中的表,并使用 Gorm 提供的方法进行 CRUD 操作。
  • dal: 数据访问对象(DAO)层,负责执行实际的数据库操作。DAO 类似于传统的 Repository,它封装了数据库操作,使得模型和业务逻辑层不需要直接与数据库打交道。
  • service: 业务逻辑层,它调用 DAO 层的方法来获取或修改数据。业务逻辑层是应用程序的核心部分,它定义了应用程序的行为和规则。
  • handler: HTTP 请求处理器,它们处理来自客户端的请求,调用服务层的方法获取所需的数据,并返回适当的响应

各层之间的一般交互流程:

  1. 客户端发送 HTTP 请求到服务器。
  2. Echo 根据路由配置找到对应的 Handler 函数。
  3. Handler 函数调用 Service 层的方法来处理请求。
  4. Service 层调用 Model 层的方法来获取或修改数据。
  5. Model 层使用 Gorm 对数据库进行操作。
  6. Service 层将处理后的数据返回给 Handler 函数。
  7. Handler 函数将数据格式化为合适的响应,并将其发送回客户端。

8. 定义模型,创建表

首先我们在model文件夹下方新建一个user.go的文件

如果数据库中还没有我们需要的表,我们可以使用 gorm 的 AutoMigrate 方法来自动创建表。这个方法会根据我们的模型来生成对应的 SQL 语句,并执行它。

代码语言:javascript复制
package model

import "time"

// 定义一个User 结构体,用来表示user表
// User 结构体表示用户表
type User struct {
  ID        uint      `gorm:"primaryKey;autoIncrement"` // 主键,自动递增
  Name      string    `gorm:"size:10;not null"`         // 用户名,最大长度10,不能为空
  Email     string    `gorm:"unique;size:20;not null"`  // 邮箱,唯一索引,最大长度20,不能为空
  Password  string    `gorm:"size:18; not null"`        // 密码,不能为空
  CreatedAt time.Time `gorm:"autoCreateTime;"`          // 在创建时,如果该字段值为零值,则使用当前时间填充
  UpdatedAt time.Time `gorm:"autoUpdateTime;"`          // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充
}

在db文件夹下的db文件中, 新增一个方法

代码语言:javascript复制
package db

import (
  "fmt"
  "gorm.io/gorm"
  "quick-start/model"
)

var DB *gorm.DB

// 自动迁移
func AutoMigrate() {
  err := DB.AutoMigrate(&model.User{})
  if err != nil {
    fmt.Println("创建表失败", err)
    panic(err)
    return
  }
  fmt.Println("创建表成功")
}

然后再去main.go文件中去使用

代码语言:javascript复制
func main() {
  // 加载环境变量
  loadEnv()
  // 初始化数据库连接
  initDataBases()
  db.AutoMigrate()
  // 启动 HTTP 服务器
  startServer()
}

9. 编写第一个请求,创建一个用户

9.1 编写handle层代码.

本部分的主要内容及时获取解析前端传过来的参数,然后传递给service层去进行操作. 最后由handle层将数据进行响应返回

新建一个user_handle.go文件

代码语言:javascript复制
package handle

import (
  "fmt"
  "github.com/labstack/echo/v4"
  "net/http"
  "quick-start/model"
  "quick-start/service"
)

// 定义一个UserHandler结构体
type UserHandler struct {
  UserService service.UserService
}

// POST  创建一个user 用户
func (h *UserHandler) Create(c echo.Context) error {
  name := c.FormValue("name")
  email := c.FormValue("email")
  password := c.FormValue("password")
  fmt.Println("111111111111111")
  fmt.Println(name, email, password)
  // 三者都不能为空,
  if name == "" || email == "" || password == "" {
    return c.JSON(http.StatusNotFound, "参数不全")
  }

  // 创建 User 模型实例
  user := &model.User{
    Name:     name,
    Email:    email,
    Password: password,
  }
  err := h.UserService.Create(user)
  if err != nil {
    return c.JSON(http.StatusInternalServerError, err)
  }
  return c.JSON(http.StatusOK, "创建成功")
}

9.2 完善service层的代码

代码语言:javascript复制
package service

import (
  "quick-start/db"
  "quick-start/model"
)

type UserService struct {
}

// 调取数据库,向数据库插入一条新数据
func (u *UserService) Create(user *model.User) error {
  return db.DB.Create(user).Error
}

9.3 路由定义

在router文件夹下面定义一个index.go 和 一个user_router.go文件.

我们目前采用这种方式管理路由

代码语言:javascript复制
router/
    user_routes.go
    order_routes.go
    product_routes.go
    index.go

其中每个模块用一个对应文件来表示. 最后在index.go 进行统一管理.

user_router.go

代码语言:javascript复制
package router

import (
  "github.com/labstack/echo/v4"
  "quick-start/handle"
)

var userHandle = handle.UserHandler{}

// 定义user相关的路由
func SetupUserRoutes(e *echo.Echo) {
  e.POST("/user/create", userHandle.Create)
}

index.go

代码语言:javascript复制
package router

import (
  "github.com/labstack/echo/v4"
)

// 统一管理路由
func SetupRoutes(e *echo.Echo) {
  SetupUserRoutes(e)
}

9.4 接口测试

打开任意一款测试软件, 我这里使用的是APIPOST 创建一个post请求, 请求地址为http://127.0.0.1:8080/user/create

创建成功

结语

在下面的文章中, 会介绍更多的ECHO 的api 以及Gorm 的使用方法. 喜欢的话, 欢迎点赞收藏~

0 人点赞