实战项目¶
📚 章节概述¶
本章将通过一个完整的实战项目,综合运用前面所学的 Go 知识。我们将构建一个 RESTful API 服务。
上图给出了任务管理 API 的核心分层与模块关系,便于把握项目组织方式。
🎯 项目目标¶
- 构建功能完整的 RESTful API
- 综合运用 Go 的核心特性
- 学习项目组织和模块化
- 掌握测试和文档编写
- 了解部署和运行
🛠️ 项目概述¶
1.1 项目简介¶
我们将构建一个 任务管理 API,具有以下功能:
- 用户认证
- 任务 CRUD 操作
- 数据库集成
- 并发处理
- 测试覆盖
1.2 技术栈¶
- Gin:Web 框架
- GORM:ORM 框架
- SQLite:数据库
- JWT:认证
📦 项目结构¶
Text Only
task-api/
├── go.mod
├── go.sum
├── main.go
├── models/
│ └── user.go
├── handlers/
│ └── task.go
├── middleware/
│ └── auth.go
├── config/
│ └── config.go
├── tests/
│ └── integration_test.go
└── README.md
🚀 项目实现¶
2.1 go.mod¶
Go
module task-api
go 1.22
require (
github.com/gin-gonic/gin v1.9.1
gorm.io/gorm v1.25.4
gorm.io/driver/sqlite v1.5.2
github.com/golang-jwt/jwt/v5 v5.0.0
)
2.2 模型定义¶
models/task.go
Go
package models
import (
"gorm.io/gorm"
"time"
)
type Task struct {
gorm.Model
Title string `json:"title"`
Description string `json:"description"`
Status string `json:"status"`
DueDate time.Time `json:"due_date"`
UserID uint `json:"user_id"`
}
type User struct {
gorm.Model
Username string `json:"username" gorm:"unique"`
Password string `json:"password"`
Tasks []Task `json:"tasks" gorm:"foreignKey:UserID"`
}
2.3 数据库初始化¶
config/database.go
Go
package config
import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"task-api/models"
)
var DB *gorm.DB
func InitDB() error {
var err error
DB, err = gorm.Open(sqlite.Open("tasks.db"), &gorm.Config{})
if err != nil {
return err
}
DB.AutoMigrate(&models.User{}, &models.Task{})
return nil
}
2.4 处理器¶
handlers/task.go
Go
package handlers
import (
"net/http"
"task-api/config"
"task-api/models"
"github.com/gin-gonic/gin"
)
func CreateTask(c *gin.Context) {
var task models.Task
if err := c.ShouldBindJSON(&task); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
config.DB.Create(&task)
c.JSON(http.StatusCreated, task)
}
func GetTasks(c *gin.Context) {
var tasks []models.Task
config.DB.Find(&tasks)
c.JSON(http.StatusOK, tasks)
}
func GetTask(c *gin.Context) {
id := c.Param("id")
var task models.Task
if err := config.DB.First(&task, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Task not found"})
return
}
c.JSON(http.StatusOK, task)
}
func UpdateTask(c *gin.Context) {
id := c.Param("id")
var task models.Task
if err := config.DB.First(&task, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Task not found"})
return
}
if err := c.ShouldBindJSON(&task); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
config.DB.Save(&task)
c.JSON(http.StatusOK, task)
}
func DeleteTask(c *gin.Context) {
id := c.Param("id")
config.DB.Delete(&models.Task{}, id)
c.Status(http.StatusNoContent)
}
2.5 中间件¶
middleware/auth.go
Go
package middleware
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"})
c.Abort()
return
}
if !strings.HasPrefix(token, "Bearer ") {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid authorization format"})
c.Abort()
return
}
// 验证 JWT token
// ...
c.Next()
}
}
2.6 主程序¶
main.go
Go
package main
import (
"task-api/config"
"task-api/handlers"
"task-api/middleware"
"github.com/gin-gonic/gin"
)
func main() {
// 初始化数据库
if err := config.InitDB(); err != nil {
panic("Failed to connect to database")
}
// 创建路由
r := gin.Default()
// 公开路由(需要实现 handlers.Register 和 handlers.Login)
// r.POST("/register", handlers.Register)
// r.POST("/login", handlers.Login)
// 认证路由
authorized := r.Group("/api")
authorized.Use(middleware.AuthMiddleware())
authorized.GET("/tasks", handlers.GetTasks)
authorized.POST("/tasks", handlers.CreateTask)
authorized.GET("/tasks/:id", handlers.GetTask)
authorized.PUT("/tasks/:id", handlers.UpdateTask)
authorized.DELETE("/tasks/:id", handlers.DeleteTask)
// 启动服务器
r.Run(":8080")
}
🧪 测试¶
3.1 集成测试¶
tests/integration_test.go
Go
package tests
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"task-api/handlers"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func TestCreateTask(t *testing.T) {
router := gin.Default()
router.POST("/tasks", handlers.CreateTask)
task := map[string]string{
"title": "Test Task",
"description": "Test Description",
"status": "pending",
}
jsonData, _ := json.Marshal(task)
req, _ := http.NewRequest("POST", "/tasks", bytes.NewBuffer(jsonData))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusCreated, w.Code)
}
📚 文档¶
4.1 README.md¶
Markdown
# Task API
一个简单的任务管理 RESTful API。
## 功能
- 用户注册和登录
- 任务的 CRUD 操作
- JWT 认证
- 数据库持久化
## 安装
```bash
go mod download
go run main.go
```
## API 端点
### 认证
- `POST /register` - 用户注册
- `POST /login` - 用户登录
### 任务
- `GET /api/tasks` - 获取所有任务
- `POST /api/tasks` - 创建任务
- `GET /api/tasks/:id` - 获取单个任务
- `PUT /api/tasks/:id` - 更新任务
- `DELETE /api/tasks/:id` - 删除任务
## 示例
```bash
# 创建任务
curl -X POST http://localhost:8080/api/tasks \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"title":"New Task","description":"Task description","status":"pending"}'
```
## 🚀 构建和部署
### 5.1 构建
```bash
# 构建可执行文件
go build -o task-api
# 交叉编译
GOOS=linux GOARCH=amd64 go build -o task-api-linux
```
### 5.2 运行
```bash
# 运行
./task-api
# 或使用 go run
go run main.go
```
📝 练习题¶
练习 1:添加新功能¶
练习 2:性能优化¶
💡 最佳实践¶
1. 错误处理¶
Go
// 好:统一的错误处理
func handleError(c *gin.Context, err error) {
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
}
// 避免:分散的错误处理
2. 配置管理¶
🎯 项目总结¶
通过这个实战项目,我们学习了:
- ✅ 项目组织和模块化
- ✅ RESTful API 设计
- ✅ 数据库集成
- ✅ 中间件使用
- ✅ 测试编写
- ✅ 文档编写
- ✅ 构建和部署
📚 扩展阅读¶
🎯 本章小结¶
本章通过一个完整的实战项目,综合运用了前面所学的 Go 知识:
- ✅ 构建了功能完整的 RESTful API
- ✅ 综合运用了 Go 的核心特性
- ✅ 学习了项目组织和模块化
- ✅ 掌握了测试和文档编写
- ✅ 了解了部署和运行
恭喜! 基础教程到此完成!继续学习进阶章节(gRPC、数据库、新特性)提升实战能力 🐹