# self_web **Repository Path**: zhanbei01/self_web ## Basic Information - **Project Name**: self_web - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-10-27 - **Last Updated**: 2025-10-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Self Web - Go Web 框架 一个轻量级、高性能的 Go Web 框架,从零开始手写实现,深入理解 Web 框架的核心原理。 ## 📋 目录 - [项目简介](#项目简介) - [核心特性](#核心特性) - [项目架构](#项目架构) - [核心模块详解](#核心模块详解) - [1. Engine 引擎](#1-engine-引擎) - [2. Router 路由系统](#2-router-路由系统) - [3. Trie 树路由匹配](#3-trie-树路由匹配) - [4. Context 上下文](#4-context-上下文) - [5. RouterGroup 路由分组](#5-routergroup-路由分组) - [6. Middleware 中间件机制](#6-middleware-中间件机制) - [7. Template 模板引擎](#7-template-模板引擎) - [8. Recovery 错误恢复](#8-recovery-错误恢复) - [快速开始](#快速开始) - [使用示例](#使用示例) --- ## 项目简介 **Self Web** 是一个手写的 Go 语言 Web 服务器框架,旨在深入理解 Web 框架的底层实现原理。项目参考了 Gin 框架的设计思想,实现了路由、中间件、分组管理、模板渲染等核心功能。 ## 核心特性 - ✅ **高效路由匹配**:基于 Trie 树(前缀树)实现的路由系统,支持动态路由和通配符 - ✅ **路由分组管理**:支持路由分组,便于对不同模块进行统一管理 - ✅ **中间件机制**:灵活的中间件支持,可在请求处理前后执行额外逻辑 - ✅ **上下文封装**:Context 封装请求和响应,提供便捷的 JSON、HTML、String 等响应方法 - ✅ **模板渲染**:支持 HTML 模板渲染和自定义模板函数 - ✅ **静态文件服务**:内置静态文件服务支持 - ✅ **错误恢复**:Panic 恢复机制,防止服务器崩溃,提高稳定性 - ✅ **请求日志**:内置日志中间件,记录请求信息 --- ## 项目架构 ``` self_web/ ├── engine.go # 核心引擎和路由分组 ├── router.go # 路由注册和处理 ├── trie.go # Trie 树路由匹配结构 ├── context.go # 上下文封装 ├── middleware.go # 中间件(日志、恢复) ├── template.go # 模板引擎相关 ├── engine_test.go # 测试文件 ├── go.mod # Go 模块文件 └── README.md # 项目文档 ``` ### 模块关系图 ``` ┌─────────────────────────────────────────────────┐ │ Engine │ │ (核心引擎,实现 http.Handler 接口) │ └────────────────┬────────────────────────────────┘ │ ┌────────┴────────┐ │ │ ┌────▼─────┐ ┌─────▼──────┐ │ Router │ │RouterGroup │ │ (路由器) │ │ (路由分组) │ └────┬─────┘ └─────┬──────┘ │ │ ┌────▼─────┐ ┌─────▼──────────┐ │ Trie │ │ Middleware │ │(前缀树) │ │ (中间件) │ └──────────┘ └────────────────┘ │ ┌────▼─────┐ │ Context │ │ (上下文) │ └──────────┘ ``` --- ## 核心模块详解 ### 1. Engine 引擎 **文件**: `engine.go` Engine 是整个框架的核心,负责: - 管理所有路由和路由分组 - 实现 `http.Handler` 接口,处理所有 HTTP 请求 - 管理全局中间件 - 管理 HTML 模板 #### 核心结构 ```go type Engine struct { router *Router // 路由器 *RouterGroup // 继承根路由分组 groups []*RouterGroup // 所有路由分组 htmlTemplate *template.Template // HTML 模板 funcMap template.FuncMap // 模板函数映射 } ``` #### 关键方法 - `NewEngine()`: 创建新的 Engine 实例 - `ServeHTTP()`: 实现 http.Handler 接口,处理 HTTP 请求 - `Start(addr string)`: 启动 Web 服务器 - `LoadHtmlGlob(pattern string)`: 加载 HTML 模板 - `SetFuncMap(funcMap)`: 设置模板自定义函数 #### 工作流程 ``` HTTP 请求 → ServeHTTP() → 匹配中间件 → 创建 Context → 路由处理 → 响应 ``` --- ### 2. Router 路由系统 **文件**: `router.go` Router 负责路由的注册、匹配和处理。 #### 核心结构 ```go type Router struct { router map[string]*node // 方法 -> Trie 树根节点 handlers map[string]HandlerFunc // 路由 -> 处理函数 } ``` #### 路由注册流程 1. 解析路由 pattern(如 `/user/:id` 或 `/static/*filepath`) 2. 将路由按 `/` 分割成路径片段 3. 插入到对应 HTTP 方法的 Trie 树中 4. 存储处理函数到 handlers map #### 支持的路由类型 - **静态路由**: `/user/list` - **动态路由**: `/user/:id` (`:id` 可匹配任意值) - **通配符路由**: `/static/*filepath` (`*filepath` 匹配剩余所有路径) #### 关键方法 - `addRoute()`: 注册路由 - `getRouter()`: 根据请求路径匹配路由,返回节点和参数 - `handler()`: 处理请求,调用匹配的处理函数 - `Static()`: 注册静态文件服务 --- ### 3. Trie 树路由匹配 **文件**: `trie.go` 使用前缀树(Trie)数据结构实现高效的路由匹配,时间复杂度为 O(m),m 为路径长度。 #### 核心结构 ```go type node struct { pattern string // 完整路由模式(叶子节点才有) part string // 当前路径片段 children []*node // 子节点 isWild bool // 是否为动态路由(: 或 *) } ``` #### Trie 树示例 ``` 注册路由: /user/list /user/:id /user/:id/profile /static/*filepath Trie 树结构: root | user / \ list :id / \ profile (pattern: /user/:id) static | *filepath ``` #### 核心算法 **插入(insert)**: ``` 1. 从根节点开始 2. 遍历路径片段 3. 查找匹配的子节点 4. 如果不存在,创建新节点 5. 递归插入下一层 6. 最后一层设置 pattern ``` **搜索(search)**: ``` 1. 从根节点开始 2. 遍历路径片段 3. 匹配所有可能的子节点(包括通配符) 4. 递归搜索 5. 返回匹配的节点和参数 ``` #### 优势 - **高效**: O(m) 时间复杂度 - **灵活**: 支持动态参数和通配符 - **直观**: 树状结构清晰表达路由层级关系 --- ### 4. Context 上下文 **文件**: `context.go` Context 封装了 HTTP 请求和响应,提供便捷的 API。 #### 核心结构 ```go type Context struct { Writer http.ResponseWriter // 响应写入器 Req *http.Request // 请求对象 Path string // 请求路径 Method string // 请求方法 Params map[string]string // 路由参数 StatusCode int // 响应状态码 handlers []HandlerFunc // 中间件和处理函数链 index int // 当前执行的处理函数索引 engine *Engine // 引擎引用 } ``` #### 提供的便捷方法 **请求相关**: - `Param(key)`: 获取路由参数(如 `/user/:id` 中的 id) - `PostForm(key)`: 获取表单参数 **响应相关**: - `String(code, format, values...)`: 返回纯文本 - `Json(code, obj)`: 返回 JSON - `HTML(code, name, data)`: 渲染 HTML 模板 - `Data(code, data)`: 返回二进制数据 - `SetStatus(code)`: 设置状态码 - `SetHeader(key, value)`: 设置响应头 **中间件相关**: - `Next()`: 执行下一个中间件或处理函数 - `Fail(code, err)`: 终止执行并返回错误 #### 使用示例 ```go func handler(c *Context) { // 获取路由参数 id := c.Param("id") // 返回 JSON c.Json(200, H{ "id": id, "message": "success", }) } ``` --- ### 5. RouterGroup 路由分组 **文件**: `engine.go` RouterGroup 实现了路由分组功能,便于对不同模块的路由进行统一管理和应用中间件。 #### 核心结构 ```go type RouterGroup struct { prefix string // 路由前缀 middlewares []HandlerFunc // 分组中间件 parentGroup *RouterGroup // 父分组 engine *Engine // 引擎引用 } ``` #### 分组机制 - **前缀继承**: 子分组自动继承父分组的前缀 - **中间件继承**: 请求会执行匹配的所有分组的中间件 - **嵌套分组**: 支持多层嵌套 #### 使用示例 ```go engine := NewEngine() // 创建 v1 分组 v1 := engine.Group("/v1") v1.Use(Logger()) // v1 分组使用日志中间件 v1.GET("/users", getUsers) v1.POST("/users", createUser) // 创建 v2 分组 v2 := engine.Group("/v2") v2.Use(Logger(), Recovery()) // v2 分组使用多个中间件 v2.GET("/users", getUsersV2) // 嵌套分组 admin := v2.Group("/admin") admin.Use(AuthMiddleware()) // admin 子分组额外使用认证中间件 admin.DELETE("/users/:id", deleteUser) ``` 实际路由: - `GET /v1/users` → 使用 Logger 中间件 - `GET /v2/users` → 使用 Logger + Recovery 中间件 - `DELETE /v2/admin/users/:id` → 使用 Logger + Recovery + Auth 中间件 --- ### 6. Middleware 中间件机制 **文件**: `middleware.go` 中间件是一个函数链,在请求处理前后执行额外逻辑。 #### 中间件类型 ```go type HandlerFunc func(c *Context) ``` #### 执行流程 ``` 请求 → 中间件1 → 中间件2 → 处理函数 → 中间件2 → 中间件1 → 响应 ↓ ↓ ↓ ↑ ↑ ↑ Next() Next() 执行 返回 返回 返回 ``` #### 内置中间件 **1. Logger() - 日志中间件** 记录请求方法、路径和状态码。 ```go func Logger() HandlerFunc { return func(c *Context) { c.Next() // 先执行后续处理 log.Printf(" - %s %s %d", c.Method, c.Path, c.StatusCode) } } ``` **2. Recovery() - 错误恢复中间件** 捕获 panic,防止服务器崩溃,并打印堆栈信息。 ```go func Recovery() HandlerFunc { return func(c *Context) { defer func() { if err := recover(); err != nil { message := fmt.Sprintf("%s", err) log.Printf("%s\n\n", trace(message)) // 打印堆栈 c.Fail(http.StatusInternalServerError, "Internal Server Error") } }() c.Next() } } ``` #### 自定义中间件示例 **认证中间件**: ```go func AuthMiddleware() HandlerFunc { return func(c *Context) { token := c.Req.Header.Get("Authorization") if token != "valid-token" { c.Json(401, H{"error": "Unauthorized"}) return // 不调用 Next(),终止执行 } c.Next() // 认证通过,继续执行 } } ``` **CORS 中间件**: ```go func CORS() HandlerFunc { return func(c *Context) { c.SetHeader("Access-Control-Allow-Origin", "*") c.SetHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE") c.Next() } } ``` --- ### 7. Template 模板引擎 **文件**: `template.go`, `engine.go` 支持 HTML 模板渲染和静态文件服务。 #### 模板功能 **加载模板**: ```go engine := NewEngine() // 设置自定义模板函数 engine.SetFuncMap(template.FuncMap{ "formatDate": func(t time.Time) string { return t.Format("2006-01-02") }, }) // 加载所有 HTML 模板 engine.LoadHtmlGlob("templates/*") ``` **渲染模板**: ```go func handler(c *Context) { c.HTML(200, "index.html", H{ "title": "首页", "users": []string{"Alice", "Bob"}, }) } ``` #### 静态文件服务 ```go // 注册静态文件路由 engine.Static("/assets", "./static") // 访问: http://localhost:8080/assets/style.css // 实际文件: ./static/style.css ``` 内部实现使用通配符路由 `/*filepath` 和 `http.FileServer`。 --- ### 8. Recovery 错误恢复 **文件**: `middleware.go` Recovery 中间件是保障服务稳定性的关键组件。 #### 核心功能 1. **捕获 Panic**: 使用 `defer + recover()` 捕获运行时 panic 2. **堆栈追踪**: 使用 `runtime.Callers()` 获取完整的调用堆栈 3. **日志记录**: 详细记录错误信息和堆栈,便于调试 4. **优雅降级**: 返回 500 错误而不是崩溃 #### 堆栈追踪实现 ```go func trace(message string) string { var pcs [32]uintptr n := runtime.Callers(3, pcs[:]) // 跳过前 3 个调用者 var str strings.Builder str.WriteString(message + "\nTraceback:") for _, pc := range pcs[:n] { fn := runtime.FuncForPC(pc) file, line := fn.FileLine(pc) str.WriteString(fmt.Sprintf("\n\t%s:%d", file, line)) } return str.String() } ``` #### 使用示例 ```go engine := NewEngine() engine.Use(Recovery()) // 全局使用恢复中间件 engine.GET("/panic", func(c *Context) { panic("something went wrong") // 不会导致服务器崩溃 }) ``` 输出日志: ``` something went wrong Traceback: /path/to/handler.go:42 /path/to/context.go:48 /path/to/router.go:51 ... ``` --- ## 快速开始 ### 安装 ```bash go get zhanbei.com/self_web ``` ### 最简示例 ```go package main import "zhanbei.com/self_web" func main() { engine := self_web.NewEngine() engine.GET("/", func(c *self_web.Context) { c.String(200, "Hello, Self Web!") }) engine.Start(":8080") } ``` --- ## 使用示例 ### 完整示例 ```go package main import ( "net/http" "time" "zhanbei.com/self_web" ) func main() { engine := self_web.NewEngine() // 全局中间件 engine.Use(self_web.Logger(), self_web.Recovery()) // 静态文件 engine.Static("/assets", "./static") // 模板设置 engine.SetFuncMap(template.FuncMap{ "now": time.Now, }) engine.LoadHtmlGlob("templates/*") // 路由注册 engine.GET("/", indexHandler) // API v1 分组 v1 := engine.Group("/api/v1") { v1.GET("/users", getUsers) v1.GET("/users/:id", getUserByID) v1.POST("/users", createUser) } // API v2 分组(带认证) v2 := engine.Group("/api/v2") v2.Use(AuthMiddleware()) { v2.GET("/users", getUsersV2) v2.PUT("/users/:id", updateUser) v2.DELETE("/users/:id", deleteUser) } // 启动服务 engine.Start(":8080") } func indexHandler(c *self_web.Context) { c.HTML(200, "index.html", self_web.H{ "title": "欢迎使用 Self Web", }) } func getUsers(c *self_web.Context) { c.Json(200, self_web.H{ "users": []string{"Alice", "Bob", "Charlie"}, }) } func getUserByID(c *self_web.Context) { id := c.Param("id") c.Json(200, self_web.H{ "id": id, "name": "Alice", }) } func createUser(c *self_web.Context) { name := c.PostForm("name") c.Json(201, self_web.H{ "message": "User created", "name": name, }) } func AuthMiddleware() self_web.HandlerFunc { return func(c *self_web.Context) { token := c.Req.Header.Get("Authorization") if token == "" { c.Json(401, self_web.H{"error": "Unauthorized"}) return } c.Next() } } ``` --- ## 技术亮点 ### 1. Trie 树路由匹配 相比 map 查找,Trie 树支持动态路由和通配符,更加灵活高效。 ### 2. 中间件洋葱模型 中间件按照注册顺序执行,通过 `Next()` 控制执行流程,实现洋葱模型。 ### 3. 路由分组 通过前缀和中间件继承,优雅地管理复杂路由结构。 ### 4. Context 设计 将请求、响应、路由参数、中间件链都封装在 Context 中,简化 API 设计。 ### 5. 错误恢复 通过 Recovery 中间件捕获 panic,确保单个请求错误不会导致整个服务崩溃。 --- ## 演进历程 1. **V1**: 基于 map 的简单路由注册和查找 2. **V2**: 引入 Context,封装请求和响应 3. **V3**: 使用 Trie 树优化路由匹配,支持动态路由 4. **V4**: 实现路由分组功能 5. **V5**: 添加中间件机制 6. **V6**: 支持模板渲染和静态文件服务 7. **V7**: 添加错误恢复机制 --- ## 总结 Self Web 是一个教学型 Web 框架,通过手写实现,深入理解了: - HTTP 服务器的工作原理 - 路由匹配算法(Trie 树) - 中间件设计模式 - 上下文封装 - 模板引擎集成 - 错误处理机制 该项目适合作为学习 Go Web 框架底层实现的参考。 --- ## 参考资料 - [Gin Web Framework](https://github.com/gin-gonic/gin) - [Go net/http 标准库](https://pkg.go.dev/net/http) - [前缀树(Trie)数据结构](https://en.wikipedia.org/wiki/Trie) --- **Author**: zhanbei **Date**: 2025/10 **License**: MIT