# hwf **Repository Path**: hwfo/hwf ## Basic Information - **Project Name**: hwf - **Description**: hello web framework - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-12-25 - **Last Updated**: 2026-01-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # HWF: 轻量 Go Web 框架 ## 项目介绍 HWF (Hello Web Framework) 是一个轻量级的 Go Web 框架,旨在提供简洁、易用且功能完善的 Web 开发体验。框架遵循模块化设计理念,支持项目与模块生命周期管理、INI 配置、路由绑定与校验、定时任务、钩子机制、统一上下文、日志按天滚动等核心功能。 ### 核心思想 - **模块化设计**:将应用拆分为多个独立模块,每个模块拥有自己的生命周期、路由、模型和定时任务 - **自动数据库表创建**:基于 GORM 模型自动创建和迁移数据库表,简化数据库操作 - **统一上下文**:提供统一的 Context 对象,方便在请求处理过程中访问日志、ORM 事务等资源 - **灵活配置**:支持 INI 格式配置文件,可通过命令行参数指定配置文件 - **丰富的中间件支持**:提供路由级中间件,支持统一处理请求日志、鉴权等功能 ## 快速使用 ### 安装 CLI 工具 ```bash go install gitee.com/hwfo/hwf/cmd/hwf@latest ``` ### 创建新项目 ```bash hwf new myapp cd myapp ``` ### 创建模块 ```bash # 在项目根目录下执行 hwf module user ``` ### 运行项目 ```bash # 使用默认配置文件 (config.ini) go run main.go # 使用指定配置文件 go run main.go config.prod.ini ``` ### 最小项目示例 ```go package main import ( "github.com/hwf" ) type MyApp struct{} func (a *MyApp) Name() string { return "myapp" } func (a *MyApp) Install(ctx *hwf.HwfCtx) error { return nil } func (a *MyApp) Init(ctx *hwf.HwfCtx) error { return nil } func (a *MyApp) Modules() []hwf.Module { return []hwf.Module{} } func (a *MyApp) Config() any { return nil } func main() { app := &MyApp{} err := hwf.Run(app) if err != nil { log.Fatalln(err.Error()) } } ``` ## 项目结构 HWF 推荐的项目目录结构如下: ``` ├── config.ini # 主配置文件 ├── main.go # 应用入口 ├── internal/ # 模块代码根目录 │ └── / # 模块目录 │ └── / # 模块目录 ├── resource/ # 嵌入式资源目录 (go:embed) ├── static/ # 静态资源目录 ├── runtime/ # 运行时目录 ├── logs/ # 日志目录 ├── manifest/ # 部署相关文件 ├── pkg/ # 项目内通用工具包 └── go.mod # Go 模块文件 ``` ### 应用接口实现 应用需要实现 `hwf.App` 接口: ```go type App interface { // Name 返回项目名称 Name() string // Install 首次运行时执行 (当 runtime/install.lock 不存在时) Install(ctx *HwfCtx) error // Init 每次运行时执行 Init(ctx *HwfCtx) error // Route 注册路由 Route(r Router) // Modules 返回需要注册的模块列表 Modules() []Module // Config 返回项目配置结构体指针,可读取ini配置文件中[app]部分的配置 Config() any } ``` ### 可选接口 - `AppTLSConfiger`: 提供 TLS 配置 - `AppJSONFormatter`: 自定义 JSON 输出格式 - `AppPackResourcer`: 提供嵌入式资源文件系统 - `ConfigFiler`: 提供配置文件路径 ## 模块接口 模块是框架的基本功能单元,每个模块需要实现 `hwf.Module` 接口: ```go type Module interface { // Name 返回模块名称 Name() string // Install 首次运行时执行 Install(ctx *HwfCtx) error // Init 每次运行时执行 Init(ctx *HwfCtx) error // Models 返回 GORM 模型结构体,用于自动创建数据库表 Models() []any // Route 注册模块路由 Route(r Router) // Cron 注册定时任务 Cron(c CronJob) // Triggers 声明模块可能触发的钩子名称 Triggers(h Hooks) // Config 返回模块配置结构体指针,可读取配置文件中[module.模块Name()]部分的配置数据 Config() any } ``` ### 模块目录结构 ``` internal// ├── init.go # 模块入口,实现 Module 接口 ├── define/ # 常量与配置类型 ├── model/ # 持久化模型 (GORM) ├── dto/ # 领域对象与 DTO ├── api/ # 接口契约与协定 ├── control/ # 路由与控制器 ├── service/ # 业务服务 ├── cron/ # 定时任务实现 └── pkg/ # 模块内通用工具 ``` ## 配置文件 HWF 使用 INI 格式的配置文件,支持多节配置和内联注释。 ### 配置文件位置 - 默认配置文件:`config.ini` - 可通过命令行参数指定:`go run main.go custom.ini` - 可通过 `ConfigFiler` 接口自定义配置文件路径 ### 核心配置节 ```ini [logging] retention_days = 7 # 日志保留天数 log_level = info # 日志级别: debug|info|warn|error|off request_log_enabled = true # 是否记录请求日志 console_enable = true # 是否输出日志到控制台 [web] addr = :8080 # 监听地址 read_timeout = 10 # 读取超时时间 (秒) write_timeout = 10 # 写入超时时间 (秒) idle_timeout = 60 # 空闲超时时间 (秒) static_dir = static # 静态文件目录 static_prefix = /static # 静态文件前缀 [paths] logs_dir = logs # 日志目录 runtime_dir = runtime # 运行时目录 [database] driver = sqlite # 数据库驱动: sqlite|mysql|postgres dsn = file:app.db?_pragma=busy_timeout(5000) # 数据源名称 max_open = 25 # 最大打开连接数 max_idle = 25 # 最大空闲连接数 log_sql = false # 是否记录 SQL 语句 ``` ### 模块配置 模块配置使用 `[module.<模块名>]` ,可在Module接口的Config中读取 ```ini [module.user] enable_email_verify = true password_min_length = 8 ``` ## 路由注册 ### 基本路由 ```go func (m *UserModule) Route(r hwf.Router) { // 基本路由 r.GET("/users", m.ListUsers) r.POST("/users", m.CreateUser) r.GET("/users/:id", m.GetUser) r.PUT("/users/:id", m.UpdateUser) r.DELETE("/users/:id", m.DeleteUser) // 匹配所有 HTTP 方法 r.Any("/health", m.HealthCheck) } ``` ### 路由组 ```go func (m *UserModule) Route(r hwf.Router) { // 创建路由组 api := r.Group("/api/v1") // 路由组中间件 api.Use(m.AuthMiddleware) // 组内路由 api.GET("/users", m.ListUsers) api.POST("/users", m.CreateUser) } ``` ### 静态文件 ```go // 在模块路由中注册 func (m *StaticModule) Route(r hwf.Router) { r.Static("/static", "static") r.File("/favicon.ico", "static/favicon.ico") } // 或通过配置文件自动注册 (推荐) // [web] // static_dir = static // static_prefix = /static ``` ### Context 使用 ```go func (m *UserModule) GetUser(c *hwf.ReqCtx) { // 获取用户 ID id := c.Query("id") // 访问框架上下文 ctx := c.Context() // 记录日志 ctx.Log().Infof("获取用户信息: %s", id) // 数据库事务 err := ctx.Transaction(func(tx *hwf.ORM) error { // 数据库操作... return nil }) // 返回 JSON 响应 c.JSON(200, map[string]interface{}{ "id": id, "name": "张三", }) } ``` ## 数据绑定 ### 基本绑定 ```go func (m *UserModule) CreateUser(c *hwf.ReqCtx) { var user dto.User // 绑定 JSON 请求体 if err := c.BindJSON(&user); err != nil { c.JSON(400, map[string]string{"error": err.Error()}) return } // 绑定 URL 查询参数 // if err := c.BindQuery(&user); err != nil { // c.JSON(400, map[string]string{"error": err.Error()}) // return // } // 绑定表单数据 // if err := c.BindForm(&user); err != nil { // c.JSON(400, map[string]string{"error": err.Error()}) // return // } // 绑定请求头 // if err := c.BindHeader(&user); err != nil { // c.JSON(400, map[string]string{"error": err.Error()}) // return // } // 处理业务逻辑... c.JSON(200, map[string]string{"message": "用户创建成功"}) } ``` ### 数据校验 ```go package entity type User struct { Username string `json:"username" validate:"required;min=3;max=20" msg:"required=用户名必填;min=用户名长度不能小于3个字符;max=用户名长度不能大于20个字符"` Email string `json:"email" validate:"required;email" msg:"required=邮箱必填;email=邮箱格式不正确"` Password string `json:"password" validate:"required;password=strong" msg:"required=密码必填;password=密码强度不足,需至少10位并包含大小写字母、数字和特殊字符"` Age int `json:"age" validate:"ge=18;le=100" msg:"ge=必须年满18岁;le=年龄不能超过100岁"` } ``` ### 校验规则 | 规则 | 描述 | 示例 | | -------- | ---------------------------------- | -------------------------------------- | | required | 必填字段 | `validate:"required"` | | min | 最小长度或值 | `validate:"min=3"` | | max | 最大长度或值 | `validate:"max=20"` | | len | 固定长度 | `validate:"len=11"` | | email | 邮箱格式 | `validate:"email"` | | eq | 等于 | `validate:"eq=admin"` | | gt | 大于 | `validate:"gt=18"` | | lt | 小于 | `validate:"lt=100"` | | ge | 大于等于 | `validate:"ge=18"` | | le | 小于等于 | `validate:"le=100"` | | password | 密码强度 | `validate:"password=strong"` | | phone | 手机号格式 | `validate:"phone"` | | date | 日期格式 (YYYY-MM-DD) | `validate:"date"` | | datetime | 日期时间格式 (YYYY-MM-DD HH:MM:SS) | `validate:"datetime"` | | idcard | 中国身份证号 | `validate:"idcard"` | | regex | 正则表达式 | `validate:"regex=^[a-z0-9_]{3,16}$"` | | in | 在指定范围内 | `validate:"in=admin,user,guest"` | | notin | 不在指定范围内 | `validate:"notin=admin"` | | ipv4 | IPv4 地址 | `validate:"ipv4"` | | ipv6 | IPv6 地址 | `validate:"ipv6"` | ### 自定义错误消息 ```go type User struct { Username string `json:"username" validate:"required;min=3" msg:"required=用户名不能为空;min=用户名至少需要3个字符"` } ``` ## 钩子 ### 注册钩子 ```go func (m *UserModule)Triggers(h hwf.Hooks) { // 注册用户创建后的钩子 h.On("user.created", func(ctx *hwf.Context, payload any) error { user := payload.(*entity.User) ctx.Log().Infof("用户创建成功,发送欢迎邮件: %s", user.Email) // 发送欢迎邮件逻辑... return nil }) } ``` ### 触发钩子 ```go func (m *UserService) CreateUser(user *entity.User) error { // 保存用户到数据库 if err := m.DB.Create(user).Error; err != nil { return err } // 触发用户创建钩子 ctx := hwf.NewContext() return hwf.GetHooks().Put("user.created", ctx, user) } ``` ## 定时任务 ### 注册定时任务 ```go func (m *UserModule) Cron(c hwf.CronJob) { // 每 5 秒执行一次 c.Add("*/5 * * * * *", func(ctx *hwf.Context) { ctx.Log().Println("每 5 秒执行一次的任务") }) // 每小时执行一次 c.Add("0 0 * * * *", func(ctx *hwf.Context) { ctx.Log().Println("每小时执行一次的任务") }) // 每天凌晨 2 点执行 c.Add("0 0 2 * * *", func(ctx *hwf.Context) { ctx.Log().Println("每天凌晨 2 点执行的任务") // 执行数据备份... }) } ``` ### Cron 表达式格式 HWF 支持 6 段 Cron 表达式(含秒): ``` 秒 分 时 日 月 星期 ``` | 字段 | 允许值 | 允许特殊字符 | | ---- | ------------ | ------------ | | 秒 | 0-59 | *, /, -, , | | 分 | 0-59 | *, /, -, , | | 时 | 0-23 | *, /, -, , | | 日 | 1-31 | *, /, -, , | | 月 | 1-12 | *, /, -, , | | 星期 | 0-6 (0=周日) | *, /, -, , | ## 日志 ### 基本日志 ```go // 在请求处理中 func (m *UserModule) GetUser(c *hwf.ReqCtx) { ctx := c.Context() ctx.Log().Debugf("调试信息: %s", c.R.URL.Path) ctx.Log().Infof("普通信息: %s", c.R.URL.Path) ctx.Log().Warnf("警告信息: %s", c.R.URL.Path) ctx.Log().Errorf("错误信息: %s", c.R.URL.Path) } // 在服务层 func (s *UserService) GetUser(id string) (*entity.User, error) { ctx := hwf.NewContext() ctx.Log().Infof("查询用户: %s", id) // ... } ``` ### 请求日志 启用请求日志记录: ```ini [logging] request_log_enabled = true ``` 请求日志格式: ``` 2023-10-01 12:00:00 [INFO] [GET] /api/v1/users 127.0.0.1 200 15ms ``` ### 日志配置 ```ini [logging] retention_days = 7 # 日志保留天数 log_level = info # 日志级别: debug|info|warn|error|off request_log_enabled = true # 是否记录请求日志 console_enable = true # 是否输出到控制台 [paths] logs_dir = logs # 日志目录 ``` ### 日志文件 日志文件按天生成,格式为 `YYYY-MM-DD.log`,存储在 `logs` 目录下(可通过配置修改)。 ## 数据库操作 ### 模型定义 ```go package model import ( "github.com/hwf" "gorm.io/gorm" ) type User struct { gorm.Model Username string `gorm:"unique;size:20" json:"username"` Email string `gorm:"unique;size:100" json:"email"` Password string `gorm:"size:100" json:"-"` } func (u *User) TableName() string { return "users" } ``` ### 自动迁移 框架会在首次启动时自动迁移所有模块的模型: ```go func (m *UserModule) Models() []any { return []any{&model.User{}} } ``` ### 事务处理 ```go func (s *UserService) CreateOrder(c *hwf.ReqCtx, order *model.Order) error { return c.Context().Transaction(func(tx *hwf.ORM) error { // 创建订单 if err := tx.Create(order).Error; err != nil { return err } // 更新库存 if err := tx.Model(&model.Product{}).Where("id = ?", order.ProductID).Update("stock", gorm.Expr("stock - ?", order.Quantity)).Error; err != nil { return err } return nil }) } ``` ## 中间件 ### 自定义中间件 ```go func (m *AuthModule) AuthMiddleware(c *hwf.ReqCtx) { // 从请求头获取 token token := c.R.Header.Get("Authorization") // 验证 token if token == "" || !m.ValidateToken(token) { c.AbortWithStatusJSON(401, map[string]string{"error": "未授权"}) return } // 继续执行后续中间件和处理函数 c.Next() } ``` ### 注册中间件 ```go func (m *AuthModule) Route(r hwf.Router) { // 全局中间件 r.Use(m.LogMiddleware) // 路由组中间件 api := r.Group("/api") api.Use(m.AuthMiddleware) // 特定路由中间件 api.GET("/admin", m.AdminMiddleware, m.AdminHandler) } ``` ## 高级特性 ### JSON 格式统一 ```go // 实现 AppJSONFormatter 接口 func (a *MyApp) JSONFormat(ctx *hwf.ReqCtx, v ...any) { var code int var data any var message string // 根据参数数量和类型统一格式化 switch len(v) { case 1: data = v[0] code = 200 message = "success" case 2: code = v[0].(int) data = v[1] message = "success" case 3: code = v[0].(int) data = v[1] message = v[2].(string) } // 统一响应格式 ctx.JSON(code, map[string]any{ "code": code, "message": message, "data": data, "time": time.Now().Unix(), }) } // 使用统一格式 func (m *UserModule) GetUser(c *hwf.ReqCtx) { user := &entity.User{ID: 1, Username: "张三"} c.JSONF(200, user, "获取用户成功") // 输出: {"code":200,"message":"获取用户成功","data":{"id":1,"username":"张三"},"time":1633046400} } ``` ### 反向代理 ```go func (m *ProxyModule) Route(r hwf.Router) { // 注册反向代理 r.Proxy("/api", "http://localhost:9000") } // 或在请求处理中动态代理 func (C *DynamicController) ProxyHandler(c *hwf.ReqCtx) { // 动态计算目标地址 target := "http://localhost:9000" // 反向代理 err := c.Proxy(target, func(p *httputil.ReverseProxy) { // 自定义代理配置 p.ModifyResponse = func(resp *http.Response) error { resp.Header.Set("X-Proxy", "HWF") return nil } }) if err != nil { c.AbortWithStatusJSON(500, map[string]string{"error": err.Error()}) } } ``` ## 中间件 HWF 提供了丰富的中间件支持,帮助你快速实现常见功能: | 中间件 | 说明 | 文档 | |-------|------|------| | jwt | JWT 认证中间件,支持 HS256 签名、自动续签 | [查看文档](https://gitee.com/hwfo/hwf/blob/main/middleware/jwt/README.md) | | token | Token 会话管理中间件,支持多种存储后端 | [查看文档](https://gitee.com/hwfo/hwf/blob/main/middleware/token/README.md) | | permission | 权限控制中间件,支持分组权限管理 | [查看文档](https://gitee.com/hwfo/hwf/blob/main/middleware/permission/README.md) | ## 工具包 HWF 提供了一系列实用工具包,可独立使用: | 工具包 | 说明 | 文档 | |-------|------|------| | hconfig | 配置管理,支持 JSON 格式配置文件 | [查看文档](https://gitee.com/hwfo/hwf/blob/main/pkg/hconfig/README.md) | | hcache | 文件缓存,支持持久化存储 | [查看文档](https://gitee.com/hwfo/hwf/blob/main/pkg/hcache/README.md) | | hcaptcha | 图形验证码生成与验证 | [查看文档](https://gitee.com/hwfo/hwf/blob/main/pkg/hcaptcha/README.md) | | hconv | 类型转换工具集 | [查看文档](https://gitee.com/hwfo/hwf/blob/main/pkg/hconv/README.md) | | hcrypt | 加密解密工具(AES/RSA) | [查看文档](https://gitee.com/hwfo/hwf/blob/main/pkg/hcrypt/README.md) | | hgenerate | 随机数/ID/序列号生成 | [查看文档](https://gitee.com/hwfo/hwf/blob/main/pkg/hgenerate/README.md) | | hhash | 哈希计算工具(MD5/SHA/HMAC) | [查看文档](https://gitee.com/hwfo/hwf/blob/main/pkg/hhash/README.md) | | hhttp | HTTP 客户端请求工具 | [查看文档](https://gitee.com/hwfo/hwf/blob/main/pkg/hhttp/README.md) | ## 贡献 欢迎提交 Issue 和 Pull Request! ## 许可证 MIT License