# fx-web-demo **Repository Path**: coderyantao/fx-web-demo ## Basic Information - **Project Name**: fx-web-demo - **Description**: 从零开始搭建一个基于 Fx 的完整 Web 项目 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-11-05 - **Last Updated**: 2025-11-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README **从零开始搭建一个基于 Fx 的完整 Web 项目**,包含: - 配置管理(Viper) - 日志(Zap) - HTTP 服务(Gin) - 用户服务(业务逻辑) - 优雅启动/关闭 - 模块化结构 - 可测试性 ------ ## 🚀 项目目标 实现一个简单的 HTTP API: ```bash GET /user → 返回 { "name": "Bob", "age": 18 } ``` 项目结构清晰,便于扩展。 ------ ## 第一步:初始化项目 ```bash mkdir fx-web-demo cd fx-web-demo go mod init fx-web-demo ``` 安装依赖: ```bash go get go.uber.org/fx go get github.com/gin-gonic/gin go get go.uber.org/zap go get github.com/spf13/viper ``` ------ ## 第二步:项目结构 ``` fx-web-demo/ ├── main.go ├── config/ │ └── config.go ├── logger/ │ └── logger.go ├── server/ │ └── server.go └── user/ ├── service.go └── handler.go ``` ------ ## 第三步:编写代码 ### 1. `config/config.go` —— 配置管理 ```go // config/config.go package config import ( "github.com/spf13/viper" ) type Config struct { Server ServerConfig `mapstructure:"server"` } type ServerConfig struct { Port int `mapstructure:"port"` } func NewConfig() (*Config, error) { viper.SetDefault("server.port", 8080) var cfg Config if err := viper.Unmarshal(&cfg); err != nil { return nil, err } return &cfg, nil } ``` > 💡 后续可扩展:从文件、环境变量加载配置。 ------ ### 2. `logger/logger.go` —— 日志 ```go // logger/logger.go package logger import "go.uber.org/zap" func NewLogger() (*zap.Logger, error) { return zap.NewDevelopment() } ``` ------ ### 3. `user/service.go` —— 用户服务 ```go // user/service.go package user type UserService struct{} func NewUserService() *UserService { return &UserService{} } func (s *UserService) GetProfile() map[string]interface{} { return map[string]interface{}{ "name": "Bob", "age": 18, } } ``` ------ ### 4. `user/handler.go` —— HTTP 处理器 ```go // user/handler.go package user import ( "net/http" "github.com/gin-gonic/gin" "go.uber.org/fx" ) type Handler struct { userSvc *UserService } func NewHandler(userSvc *UserService) *Handler { return &Handler{userSvc: userSvc} } func (h *Handler) RegisterRoutes(r *gin.Engine) { r.GET("/user", h.getUser) } func (h *Handler) getUser(c *gin.Context) { c.JSON(http.StatusOK, h.userSvc.GetProfile()) } // 用于 Fx 注入 gin.Engine 并注册路由 func RegisterRoutes(lc fx.Lifecycle, router *gin.Engine, handler *Handler) { handler.RegisterRoutes(router) } ``` ------ ### 5. `server/server.go` —— HTTP 服务器 ```go // server/server.go package server import ( "context" "net/http" "time" "github.com/gin-gonic/gin" "go.uber.org/fx" "go.uber.org/zap" ) type Server struct { httpSrv *http.Server logger *zap.Logger } func NewServer(config *config.Config, logger *zap.Logger, router *gin.Engine) *Server { srv := &http.Server{ Addr: fmt.Sprintf(":%d", config.Server.Port), Handler: router, } return &Server{ httpSrv: srv, logger: logger, } } func (s *Server) Start(lc fx.Lifecycle) { lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { s.logger.Info("Starting HTTP server", zap.String("addr", s.httpSrv.Addr)) go func() { if err := s.httpSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed { s.logger.Fatal("HTTP server failed", zap.Error(err)) } }() return nil }, OnStop: func(ctx context.Context) error { s.logger.Info("Shutting down HTTP server...") ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() return s.httpSrv.Shutdown(ctx) }, }) } ``` > ⚠️ 注意:需要导入 `config` 包,所以加一行: ```go import ( "fmt" "fx-web-demo/config" // ← 添加这一行 // 其他 import ) ``` ------ ### 6. `main.go` —— 应用入口 ```go // main.go package main import ( "fx-web-demo/config" "fx-web-demo/logger" "fx-web-demo/server" "fx-web-demo/user" "github.com/gin-gonic/gin" "go.uber.org/fx" "go.uber.org/zap" ) func main() { app := fx.New( // 配置 & 日志 fx.Provide(config.NewConfig), fx.Provide(logger.NewLogger), // Gin 路由(作为依赖提供) fx.Provide(func() *gin.Engine { return gin.Default() }), // 用户模块 fx.Provide(user.NewUserService), fx.Provide(user.NewHandler), fx.Invoke(user.RegisterRoutes), // HTTP 服务器 fx.Provide(server.NewServer), fx.Invoke(server.Start), // 可选:美化日志 fx.WithLogger(func(log *zap.Logger) fx.Printer { return &fx.ZapLogger{Logger: log} }), ) app.Run() } ``` ------ ## 第四步:运行! ```bash go run main.go ``` 你应该看到: ``` INFO Starting HTTP server {"addr": ":8080"} ``` 然后访问: ```bash curl http://localhost:8080/user ``` 输出: ```json {"age":18,"name":"Bob"} ``` 按 `Ctrl+C` 退出,会看到优雅关闭日志。 ------ ## ✅ 你已经完成了! 这个项目展示了 Fx 的核心能力: - 自动依赖注入(UserService → Handler → Router → Server) - 生命周期管理(优雅启动/关闭) - 模块化(每个功能独立) - 可测试(每个构造函数可单独测试) ------ ## 🔜 下一步建议 1. **添加数据库**(如 SQLite + GORM) 2. **写单元测试**(用 `fxtest`) 3. **从 YAML 文件加载配置** 4. **拆分为多个 `fx.Module`** ------ 需要我帮你: - 添加数据库? - 写测试? - 生成项目脚手架代码? - 解释某一部分细节? 随时告诉我!🎉