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)
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):用于请求 / 响应的数据格式(与实体解耦,避免直接暴露数据库字段)
Go
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 层实体:
Go
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 层,实现核心业务逻辑(如数据校验、业务规则、数据组装):
Go
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 层(控制层)
仅负责请求解析和调度,无任何业务逻辑:
Go
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)
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 层返回结果:
Go
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 层校验:
Go
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)
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)
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 崩溃,捕获异常并返回友好响应:
Go
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 路由分发:
Go
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)
启动服务器,加载路由,是程序的唯一入口:
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)
}
}