RESTful 核心原则(复用但适配 MVC)
- 资源为核心:URI 仅表示资源(/users、/users/1),不包含操作
- HTTP 方法映射操作:GET(查)、POST(增)、PUT(全量改)、DELETE(删)
- 无状态:请求携带所有必要信息,控制器不存储会话
- 响应标准化:由 View 层统一封装 JSON 格式
一、分层架构
| 分层 | 核心职责 | 对应目录 | 与传统 MVC 的映射 |
|---|---|---|---|
| Controller(控制层) | 接收 HTTP 请求、解析参数、调用 Service、通过 View 返回响应(仅做 “请求调度”) | controller/ | MVC 的 Controller |
| Service(服务层) | 核心业务逻辑(如权限校验、数据组装、业务规则),不直接操作数据存储 | service/ | MVC 的 Model(业务部分) |
| Repository(数据层) | 仅负责数据访问(数据库 / 缓存 CRUD),无业务逻辑 | repository/ | MVC 的 Model(数据部分) |
| Model(模型层) | 数据结构定义(实体、请求 / 响应 DTO)、常量、枚举 | model/ | MVC 的 Model(结构部分) |
| View(视图层) | API 响应封装(JSON 标准化输出) | view/ | MVC 的 View(适配 API) |
| Router(路由层) | RESTful 路由注册、请求分发 | router/ | 通用扩展层 |
| Middleware(中间件) | 通用横切逻辑(日志、跨域、异常捕获、认证) | middleware/ | 通用扩展层 |
| Utils(工具层) | 通用工具函数(校验、加密、日期处理) | utils/ | 通用扩展层 |
二、项目结构
go-restful-standard/ ├── main.go // 入口文件 ├── controller/ // 控制层 │ └── user_controller.go // 用户接口控制器 ├── service/ // 服务层(业务逻辑) │ └── user_service.go // 用户业务逻辑 ├── repository/ // 数据访问层 │ └── user_repo.go // 用户数据CRUD ├── model/ // 模型层(数据结构) │ ├── entity/ // 实体(对应数据库表) │ │ └── user.go │ └── dto/ // 请求/响应DTO(数据传输对象) │ └── user_dto.go ├── view/ // 视图层(响应封装) │ └── response.go ├── router/ // 路由层 │ └── router.go ├── middleware/ // 中间件 │ ├── logging.go │ ├── cors.go │ └── recover.go └── utils/ // 工具层 └── validator.go三、完整代码实现
步骤 1:初始化项目
mkdir go-restful-standard && cd go-restful-standard go mod init go-restful-standard步骤 2:Model 层(纯数据结构)
2.1 实体定义(model/entity/user.go)
package entity // User 用户实体(对应数据库表结构) type User struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Age int `json:"age"` }2.2 DTO 定义(model/dto/user_dto.go)
DTO(Data Transfer Object):用于请求 / 响应的数据格式(与实体解耦,避免直接暴露数据库字段)
package dto // CreateUserRequest 创建用户请求DTO type CreateUserRequest struct { Username string `json:"username" validate:"required,min=2,max=20"` Email string `json:"email" validate:"required,email"` Age int `json:"age" validate:"gte=0,lte=150"` } // UpdateUserRequest 更新用户请求DTO type UpdateUserRequest struct { Username string `json:"username" validate:"omitempty,min=2,max=20"` Email string `json:"email" validate:"omitempty,email"` Age int `json:"age" validate:"omitempty,gte=0,lte=150"` } // UserResponse 用户响应DTO type UserResponse struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` Age int `json:"age"` }步骤 3:Repository 层(数据访问层)
仅负责数据 CRUD,无任何业务逻辑,依赖 Model 层实体:
package repository import ( "errors" "sync" "go-restful-standard/model/entity" ) // UserRepository 用户数据访问接口 type UserRepository interface { GetAll() ([]entity.User, error) GetByID(id int) (entity.User, error) Create(user entity.User) (entity.User, error) Update(id int, user entity.User) (entity.User, error) Delete(id int) error } // userRepo 实现UserRepository接口(模拟内存数据库) type userRepo struct { users map[int]entity.User nextID int mutex sync.Mutex } // NewUserRepository 创建UserRepository实例 func NewUserRepository() UserRepository { return &userRepo{ users: map[int]entity.User{ 1: {ID: 1, Username: "zhangsan", Email: "zhangsan@example.com", Age: 20}, 2: {ID: 2, Username: "lisi", Email: "lisi@example.com", Age: 22}, }, nextID: 3, } } // GetAll 获取所有用户 func (r *userRepo) GetAll() ([]entity.User, error) { r.mutex.Lock() defer r.mutex.Unlock() list := make([]entity.User, 0, len(r.users)) for _, u := range r.users { list = append(list, u) } return list, nil } // GetByID 根据ID获取用户 func (r *userRepo) GetByID(id int) (entity.User, error) { r.mutex.Lock() defer r.mutex.Unlock() u, exist := r.users[id] if !exist { return entity.User{}, errors.New("用户不存在") } return u, nil } // Create 创建用户 func (r *userRepo) Create(user entity.User) (entity.User, error) { r.mutex.Lock() defer r.mutex.Unlock() user.ID = r.nextID r.users[r.nextID] = user r.nextID++ return user, nil } // Update 更新用户 func (r *userRepo) Update(id int, user entity.User) (entity.User, error) { r.mutex.Lock() defer r.mutex.Unlock() if _, exist := r.users[id]; !exist { return entity.User{}, errors.New("用户不存在") } user.ID = id r.users[id] = user return user, nil } // Delete 删除用户 func (r *userRepo) Delete(id int) error { r.mutex.Lock() defer r.mutex.Unlock() if _, exist := r.users[id]; !exist { return errors.New("用户不存在") } delete(r.users, id) return nil }步骤 4:Service 层(业务逻辑层)
依赖 Repository 层,实现核心业务逻辑(如数据校验、业务规则、数据组装):
package service import ( "errors" "go-restful-standard/model/dto" "go-restful-standard/model/entity" "go-restful-standard/repository" ) // UserService 用户服务接口(定义业务方法) type UserService interface { GetAllUsers() ([]dto.UserResponse, error) GetUserByID(id int) (dto.UserResponse, error) CreateUser(req dto.CreateUserRequest) (dto.UserResponse, error) UpdateUser(id int, req dto.UpdateUserRequest) (dto.UserResponse, error) DeleteUser(id int) error } // userService 实现UserService接口 type userService struct { userRepo repository.UserRepository // 依赖数据访问层 } // NewUserService 创建UserService实例(依赖注入) func NewUserService(ur repository.UserRepository) UserService { return &userService{ userRepo: ur, } } // GetAllUsers 获取所有用户(业务逻辑:无,仅组装响应) func (s *userService) GetAllUsers() ([]dto.UserResponse, error) { // 调用数据访问层 entities, err := s.userRepo.GetAll() if err != nil { return nil, errors.New("获取用户列表失败:" + err.Error()) } // 业务逻辑:实体转响应DTO(核心组装逻辑) resp := make([]dto.UserResponse, 0, len(entities)) for _, u := range entities { resp = append(resp, dto.UserResponse{ ID: u.ID, Username: u.Username, Email: u.Email, Age: u.Age, }) } return resp, nil } // GetUserByID 根据ID获取用户(业务逻辑:无) func (s *userService) GetUserByID(id int) (dto.UserResponse, error) { entity, err := s.userRepo.GetByID(id) if err != nil { return dto.UserResponse{}, err } return dto.UserResponse{ ID: entity.ID, Username: entity.Username, Email: entity.Email, Age: entity.Age, }, nil } // CreateUser 创建用户(业务逻辑:邮箱唯一性校验) func (s *userService) CreateUser(req dto.CreateUserRequest) (dto.UserResponse, error) { // 业务逻辑1:校验邮箱是否已存在 allUsers, _ := s.userRepo.GetAll() for _, u := range allUsers { if u.Email == req.Email { return dto.UserResponse{}, errors.New("邮箱已被注册") } } // 业务逻辑2:请求DTO转实体 entity := entity.User{ Username: req.Username, Email: req.Email, Age: req.Age, } // 调用数据访问层 createdEntity, err := s.userRepo.Create(entity) if err != nil { return dto.UserResponse{}, errors.New("创建用户失败:" + err.Error()) } // 组装响应DTO return dto.UserResponse{ ID: createdEntity.ID, Username: createdEntity.Username, Email: createdEntity.Email, Age: createdEntity.Age, }, nil } // UpdateUser 更新用户(业务逻辑:非空字段更新) func (s *userService) UpdateUser(id int, req dto.UpdateUserRequest) (dto.UserResponse, error) { // 业务逻辑1:获取原用户 oldEntity, err := s.userRepo.GetByID(id) if err != nil { return dto.UserResponse{}, err } // 业务逻辑2:仅更新非空字段 if req.Username != "" { oldEntity.Username = req.Username } if req.Email != "" { oldEntity.Email = req.Email } if req.Age != 0 { oldEntity.Age = req.Age } // 调用数据访问层 updatedEntity, err := s.userRepo.Update(id, oldEntity) if err != nil { return dto.UserResponse{}, errors.New("更新用户失败:" + err.Error()) } return dto.UserResponse{ ID: updatedEntity.ID, Username: updatedEntity.Username, Email: updatedEntity.Email, Age: updatedEntity.Age, }, nil } // DeleteUser 删除用户(业务逻辑:无) func (s *userService) DeleteUser(id int) error { return s.userRepo.Delete(id) }步骤 5:Controller 层(控制层)
仅负责请求解析和调度,无任何业务逻辑:
package controller import ( "encoding/json" "net/http" "strconv" "go-restful-standard/model/dto" "go-restful-standard/service" "go-restful-standard/utils" "go-restful-standard/view" ) // UserController 用户控制器 type UserController struct { userService service.UserService // 依赖服务层 } // NewUserController 创建控制器实例(依赖注入) func NewUserController(us service.UserService) *UserController { return &UserController{ userService: us, } } // GetAllUsers 处理GET /users func (c *UserController) GetAllUsers(w http.ResponseWriter, r *http.Request) { // 调用服务层 users, err := c.userService.GetAllUsers() if err != nil { view.Error(w, 500, err.Error()) return } view.Success(w, users, "获取所有用户成功") } // GetUserByID 处理GET /users/{id} func (c *UserController) GetUserByID(w http.ResponseWriter, r *http.Request) { // 解析参数 idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { view.Error(w, 400, "无效的用户ID:必须是数字") return } // 调用服务层 user, err := c.userService.GetUserByID(id) if err != nil { view.Error(w, 404, err.Error()) return } view.Success(w, user, "获取用户成功") } // CreateUser 处理POST /users func (c *UserController) CreateUser(w http.ResponseWriter, r *http.Request) { // 解析请求体 var req dto.CreateUserRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { view.Error(w, 400, "请求参数格式错误:"+err.Error()) return } defer r.Body.Close() // 参数校验 if err := utils.ValidateStruct(req); err != nil { view.Error(w, 400, "参数校验失败:"+err.Error()) return } // 调用服务层 user, err := c.userService.CreateUser(req) if err != nil { view.Error(w, 400, err.Error()) return } view.Success(w, user, "创建用户成功") } // UpdateUser 处理PUT /users/{id} func (c *UserController) UpdateUser(w http.ResponseWriter, r *http.Request) { // 解析ID idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { view.Error(w, 400, "无效的用户ID:必须是数字") return } // 解析请求体 var req dto.UpdateUserRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { view.Error(w, 400, "请求参数格式错误:"+err.Error()) return } defer r.Body.Close() // 参数校验 if err := utils.ValidateStruct(req); err != nil { view.Error(w, 400, "参数校验失败:"+err.Error()) return } // 调用服务层 user, err := c.userService.UpdateUser(id, req) if err != nil { view.Error(w, 404, err.Error()) return } view.Success(w, user, "更新用户成功") } // DeleteUser 处理DELETE /users/{id} func (c *UserController) DeleteUser(w http.ResponseWriter, r *http.Request) { // 解析ID idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { view.Error(w, 400, "无效的用户ID:必须是数字") return } // 调用服务层 if err := c.userService.DeleteUser(id); err != nil { view.Error(w, 404, err.Error()) return } view.Success(w, nil, "删除用户成功") }步骤 6: 路由层(router/router.go)
package router import ( "net/http" "go-restful-standard/controller" "go-restful-standard/middleware" "go-restful-standard/repository" "go-restful-standard/service" ) func NewRouter() http.Handler { // 依赖注入链:Repo → Service → Controller userRepo := repository.NewUserRepository() userService := service.NewUserService(userRepo) userController := controller.NewUserController(userService) mux := http.NewServeMux() // 注册RESTful路由 mux.HandleFunc("GET /users", userController.GetAllUsers) mux.HandleFunc("GET /users/{id}", userController.GetUserByID) mux.HandleFunc("POST /users", userController.CreateUser) mux.HandleFunc("PUT /users/{id}", userController.UpdateUser) mux.HandleFunc("DELETE /users/{id}", userController.DeleteUser) // 应用中间件 chain := middleware.Recover(middleware.CORS(middleware.Logging(mux))) return chain }1.http.NewServeMux()(多路复用器)
- 作用:Go 标准库内置的路由核心组件,本质是一个 “路由规则映射表”,存储
{方法+路径: 处理器函数}的键值对; - 核心能力:
- 匹配请求的
Method + Path,找到对应的处理器函数; - 支持路径参数(Go 1.22+,如
/users/{id}); - 支持通配符(如
/users/*);
- 匹配请求的
- 对比框架:无需引入
gorilla/mux等第三方路由库,Go 1.22 + 的ServeMux已满足 RESTful 路由需求。
2.mux.HandleFunc(rule, handler)(路由注册)
- 规则格式(Go 1.22+):
"METHOD PATH"(如"GET /users"),是实现 RESTful 的核心语法;- 低版本 Go(<1.22)不支持该格式,需通过
r.Method手动判断(参考扩展章节);
- 低版本 Go(<1.22)不支持该格式,需通过
- 路径参数:
/users/{id}中的{id}是动态路径参数,Controller 中可通过r.PathValue("id")获取; - 处理器要求:
handler必须是func(w http.ResponseWriter, r *http.Request)类型(即http.HandlerFunc),这也是 Controller 方法的签名规范。
3. 中间件编排(middleware.Recover(middleware.CORS(middleware.Logging(mux))))
- 执行顺序:从外到内(右→左),请求进来时的执行流程:
Recover → CORS → Logging → 业务路由(mux);响应返回时的执行流程:业务路由 → Logging → CORS → Recover; - 各中间件作用:
Logging:记录请求方法、路径、耗时;CORS:处理跨域请求头和 OPTIONS 预检请求;Recover:捕获业务路由中的 panic,防止程序崩溃;
- 设计原理:中间件本质是 “高阶函数”,接收一个
http.Handler,返回一个新的http.Handler,通过嵌套实现多层包装。
4. 依赖注入链(userRepo → userService → userController)
- 核心思想:“依赖倒置”,上层(Controller)不直接创建下层(Service)实例,而是通过构造函数接收,降低耦合;
- 优势:
- 测试友好:单元测试时可传入 Mock 的 Service/Repo,无需依赖真实数据库;
- 扩展灵活:替换 Service/Repo 实现时,只需修改构造函数参数,无需改动 Controller 逻辑;
- 统一管理:所有层的初始化集中在 Router 层,避免代码分散。
步骤 7:View 层实现(响应封装)
统一 API 响应格式,Controller 层通过 View 层返回结果:
package view import ( "encoding/json" "net/http" ) // Response 标准化JSON响应结构 type Response struct { Code int `json:"code"` // 自定义业务码(200成功,非200失败) Message string `json:"message"` // 提示信息 Data interface{} `json:"data"` // 响应数据(可选) } // Success 成功响应 func Success(w http.ResponseWriter, data interface{}, message ...string) { msg := "操作成功" if len(message) > 0 && message[0] != "" { msg = message[0] } res := Response{ Code: 200, Message: msg, Data: data, } sendJSON(w, http.StatusOK, res) } // Error 失败响应 func Error(w http.ResponseWriter, code int, message string) { res := Response{ Code: code, Message: message, Data: nil, } sendJSON(w, http.StatusOK, res) } // sendJSON 底层JSON发送函数 func sendJSON(w http.ResponseWriter, httpCode int, data interface{}) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(httpCode) if err := json.NewEncoder(w).Encode(data); err != nil { http.Error(w, "响应序列化失败", http.StatusInternalServerError) } }步骤 8:Utils 层(参数校验工具)
封装通用参数校验逻辑,简化 Controller 层校验:
package utils import ( "errors" "github.com/go-playground/validator/v10" // 轻量级校验库(仅工具依赖,非Web框架) ) // 全局校验器实例 var validate = validator.New() // ValidateStruct 校验结构体字段 func ValidateStruct(s interface{}) error { err := validate.Struct(s) if err != nil { // 解析校验错误信息 var errMsgs []string for _, e := range err.(validator.ValidationErrors) { errMsgs = append(errMsgs, e.Field()+"字段校验失败:"+e.Tag()) } return errors.New(errMsgs[0]) // 简化返回第一个错误,可扩展为多错误 } return nil }安装校验库:
go get github.com/go-playground/validator/v10
步骤 9:Middleware 层(通用逻辑)
9.1 日志中间件(middleware/logging.go)
package middleware import ( "log" "net/http" "time" ) // Logging 记录请求日志(方法、路径、耗时) func Logging(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() log.Printf("[REQUEST] %s %s", r.Method, r.URL.Path) // 调用下一个处理器 next.ServeHTTP(w, r) // 后置逻辑 duration := time.Since(start) log.Printf("[RESPONSE] %s %s - %s", r.Method, r.URL.Path, duration) }) }9.2 跨域中间件(middleware/cors.go)
package middleware import ( "net/http" ) // CORS 处理跨域请求 func CORS(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 设置跨域响应头 w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") // 处理OPTIONS预检请求 if r.Method == http.MethodOptions { w.WriteHeader(http.StatusOK) return } next.ServeHTTP(w, r) }) }9.3 异常捕获中间件(middleware/recover.go)
防止程序 panic 崩溃,捕获异常并返回友好响应:
package middleware import ( "log" "net/http" "go-restful-mvc/view" ) // Recover 捕获处理器中的panic func Recover(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if err := recover(); err != nil { log.Printf("[PANIC] %v", err) view.Error(w, 500, "服务器内部错误") } }() next.ServeHTTP(w, r) }) }步骤 10:Router 层(RESTful 路由注册)
将 HTTP 请求(方法 + 路径)映射到对应 Controller 方法,实现 RESTful 路由分发:
package router import ( "net/http" "go-restful-mvc/controller" "go-restful-mvc/middleware" "go-restful-mvc/model" ) // NewRouter 创建路由实例(注册路由+应用中间件) func NewRouter() http.Handler { // 1. 初始化依赖(Controller依赖Model) userService := model.NewUserService() userController := controller.NewUserController(userService) // 2. 创建原生多路复用器 mux := http.NewServeMux() // 3. 注册RESTful路由(HTTP方法+URI → Controller方法) // 用户资源路由 mux.HandleFunc("GET /users", userController.GetAllUsers) mux.HandleFunc("GET /users/{id}", userController.GetUserByID) mux.HandleFunc("POST /users", userController.CreateUser) mux.HandleFunc("PUT /users/{id}", userController.UpdateUser) mux.HandleFunc("DELETE /users/{id}", userController.DeleteUser) // 4. 应用中间件(顺序:外层先执行 → Recover → CORS → Logging → 业务路由) chain := middleware.Recover(middleware.CORS(middleware.Logging(mux))) return chain }步骤 11:入口文件(main.go)
启动服务器,加载路由,是程序的唯一入口:
package main import ( "log" "net/http" "go-restful-mvc/router" ) func main() { // 1. 创建路由 r := router.NewRouter() // 2. 服务器配置 addr := ":8080" log.Printf("Server starting on http://localhost%s", addr) // 3. 启动HTTP服务器 if err := http.ListenAndServe(addr, r); err != nil && err != http.ErrServerClosed { log.Fatalf("服务器启动失败:%v", err) } }