Go 语言在 Web 后端开发领域越来越受欢迎,原因是:简单、高并发、性能强、部署方便。本文将系统总结 Go Web 开发的完整流程,涵盖从项目初始化到前后端交互以及数据库层设计的全流程。
一、Go Web 项目整体架构
采用典型的 MVC + 分层开发:
project │-- main.go // 程序入口 │ ├── router/ // 路由注册 ├── controller/ // 控制器层(HTTP 处理、参数解析、响应) ├── service/ // 业务逻辑层 ├── dao/ // 数据访问层(数据库操作) ├── model/ // 数据模型定义(结构体的定义) ├── config/ // 配置文件(数据库配置等) └── views/ // HTML 模板(如果使用 SSR)
二、开发流程概览
接收请求 → 路由 → Controller → Service → DAO → DB
↓
返回结果
三、RESTful 接口设计
什么是 RESTful 架构
(1)每一个 URI 代表一种资源;
URI(URL)不是用来描述动作,而是用来定位资源对象。
(2)客户端和服务器之间,传递这种资源的某种表现层;
客户端与服务器不直接传递 "资源本身",而是传递 "资源的表现层"
资源是抽象的 "数据本身",不依赖任何展示形式。
表现层(Representation):资源的 "具体展示形式",是资源在网络中传输的 "载体"(本质是数据的序列化格式)。常见的有JSON ,XML,HTML。
(3)客户端通过四个 HTTP 动词,对服务器端资源进行操作,实现 "表现层状态转化"。
操作 方法 URL 示例 描述 查询用户 GET /user/123 查询 ID=123 用户 新增用户 POST /adduser/123 创建用户 修改用户 PUT /moduser/123 更新用户信息 删除用户 DELETE /deluser/123 删除用户
REST 的核心是:
- URL 表示资源,而不是动作。 也就是说URL中并不包含用户的动作。
- 资源通过 URI 唯一标识,客户端使用 HTTP 方法对资源进行操作,并在资源表现层之间转换状态。
非RESTful风格
Go
package router
import (
"Web/controller"
"net/http"
)
func InitRouter() {
http.HandleFunc("/user/get", controller.GetUser)
http.HandleFunc("/user/create", controller.CreateUser)
http.HandleFunc("/user/update", controller.UpdateUser)
http.HandleFunc("/user/delete", controller.DeleteUser)
}
RESTful风格
用gin框架来实现RESTful风格
Go
package router
import (
"Web/controller"
"github.com/gin-gonic/gin"
)
func InitRouter() *gin.Engine {
r := gin.Default()
user := r.Group("/api/user")
{
user.GET("/:id", controller.GetUser) // 查询单个
user.GET("", controller.GetUserList) // 查询列表
user.POST("", controller.CreateUser) // 创建
user.PUT("/:id", controller.UpdateUser) // 更新
user.DELETE("/:id", controller.DeleteUser) // 删除
}
return r
}
用原生的方式来实现RESTful
Go
package router
import (
"Web/controller"
"net/http"
)
func InitRouter() {
http.HandleFunc("/api/user", controller.UserHandler) // 处理列表 & 创建
http.HandleFunc("/api/user/", controller.UserIDHandler) // 处理查询/更新/删除
}
Go
package controller
import (
"fmt"
"net/http"
"strings"
)
func UserHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
GetUserList(w, r)
case "POST":
CreateUser(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}
func UserIDHandler(w http.ResponseWriter, r *http.Request) {
id := strings.TrimPrefix(r.URL.Path, "/api/user/") // 获取 id
switch r.Method {
case "GET":
GetUser(w, r, id)
case "PUT":
UpdateUser(w, r, id)
case "DELETE":
DeleteUser(w, r, id)
default:
http.Error(w, "method not allowed", 405)
}
}
四、Go Web 的分层设计
1. model:数据模型层
用于定义数据库表映射的结构体
Go
package model
type User struct {
ID int64 `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
}
2. dao:数据库访问层
负责与 MySQL 进行交互:
Go
package dao
import (
"Web/model"
"database/sql"
)
var DB *sql.DB
func GetUser(id int64) (*model.User, error) {
user := &model.User{}
err := DB.QueryRow("SELECT id, username, password,email FROM user WHERE id = ?", id).
Scan(&user.ID, &user.Username, &user.Password, &user.Email)
return user, err
}
func CreateUser(u *model.User) error {
_, err := DB.Exec("INSERT INTO user (username, password,email) VALUES (?, ?,?)",
u.Username, u.Password, u.Email)
return err
}
func UpdateUser(u *model.User) error {
_, err := DB.Exec("UPDATE user SET username=?, password=?,email=? WHERE id=?",
u.Username, u.Password, u.Email, u.ID)
return err
}
func DeleteUser(id int64) error {
_, err := DB.Exec("DELETE FROM user WHERE id=?", id)
return err
}
DAO 层只做数据库 CRUD,不处理任何业务逻辑。
3. service:业务层
对业务逻辑进行处理,例如校验、密码加密等:
Go
package service
import (
"Web/dao"
"Web/model"
)
func GetUser(id int64) (*model.User, error) {
return dao.GetUser(id)
}
func CreateUser(u *model.User) error {
return dao.CreateUser(u)
}
func UpdateUser(u *model.User) error {
return dao.UpdateUser(u)
}
func DeleteUser(id int64) error {
return dao.DeleteUser(id)
}
Service 层不关心 HTTP,只关心业务。
4. controller:控制器层
接收 HTTP 请求、解析参数、返回响应:
Go
package controller
import (
"Web/model"
"Web/service"
"encoding/json"
"log"
"net/http"
"strconv"
)
func GetUser(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
log.Println("转换为int类型错误")
http.Error(w, "Invalid user ID", http.StatusBadRequest) // 400 错误
return
}
user, err := service.GetUser(id)
if err != nil {
http.Error(w, "User Not Found", 404)
return
}
encoder := json.NewEncoder(w)
err = encoder.Encode(user)
if err != nil {
log.Println("写入流中错误", err)
return
}
}
func CreateUser(w http.ResponseWriter, r *http.Request) {
var u model.User
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&u)
if err != nil {
http.Error(w, "Invalid JSON data: "+err.Error(), http.StatusBadRequest)
return
}
if err := service.CreateUser(&u); err != nil {
http.Error(w, "Create failed", 500)
return
}
_, err = w.Write([]byte("ok"))
if err != nil {
log.Println("向流写入ok错误", err)
return
}
}
func UpdateUser(w http.ResponseWriter, r *http.Request) {
var u model.User
err := json.NewDecoder(r.Body).Decode(&u)
if err != nil {
http.Error(w, "Json转结构体错误: "+err.Error(), http.StatusBadRequest)
return
}
if err := service.UpdateUser(&u); err != nil {
http.Error(w, "Update failed", 500)
return
}
_, err = w.Write([]byte("ok"))
if err != nil {
log.Println("向流写入ok错误", err)
return
}
}
func DeleteUser(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
log.Println("转换为int类型错误")
http.Error(w, "Invalid user ID", http.StatusBadRequest)
return
}
if err := service.DeleteUser(id); err != nil {
http.Error(w, "Delete failed", 500)
return
}
_, err = w.Write([]byte("ok"))
if err != nil {
log.Println("向流写入ok错误", err)
return
}
}
5.config:配置文件层
以连接mysql为例
Go
package config
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
var DB *sql.DB
func InitDB() {
db, err := sql.Open("mysql", "你的用户名:你的密码@tcp(127.0.0.1:3306)/要用的数据库名")
if err != nil {
panic(err)
}
DB = db
}
6.router:路由映射层
定义 URL 与控制器映射,即负责分发请求。一般要符合RESTful风格设计(详细请见本文第三部分)
五、前后端交互
采用JSON作为前后端数据交换格式,确保结构清晰且易于解析。前端发送请求时,后端将数据封装为JSON对象,后端返回时统一包含**code(状态码)、data(业务数据)、message(提示信息)**字段。
{
"code": 200,
"data": {...},
"message": "success"
}
| 字段 | 含义 | 示例 |
|---|---|---|
| code | 业务状态码 | 200 成功,400 参数错误,500 服务器错误 |
| data | 返回内容(结构体或数组) | { "id":1, "name":"Tom" } |
| message | 提示语 | "操作成功"或 "参数错误" |
Go
package response
import "github.com/gin-gonic/gin"
type Result struct {
Code int `json:"code"`
Data interface{} `json:"data,omitempty"`
Message string `json:"message"`
}
func Success(c *gin.Context, data interface{}) {
c.JSON(200, Result{200, data, "success"})
}
func Fail(c *gin.Context, message string) {
c.JSON(400, Result{400, nil, message})
}
一般创建如此的响应结构来封装。
六、服务启动 main.go
Go
package main
import (
"Web/config"
"Web/router"
"fmt"
_ "github.com/go-sql-driver/mysql"
"log"
"net/http"
)
func main() {
config.InitDB()
router.InitRouter()
fmt.Println("server running on :8080")
err := http.ListenAndServe(":8080", cors(http.DefaultServeMux))
if err != nil {
log.Println(err)
return
}
}
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", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if r.Method == "OPTIONS" {
return
}
next.ServeHTTP(w, r)
})
}
cors函数用于解决前端和 Go 后端之间的跨域请求问题,让不同域名 / 端口的前端页面能正常调用 Go 后端接口。
七、项目部署
分别编写前后端的Dockerfile以及Nginx 反向代理配置
再通过容器编排实现。
自动启动:
-
MySQL 数据库
-
Go Web 后端服务
-
Vue/React 前端静态站点 (Nginx 托管)
-
反向代理 Nginx(转发 /api 到 Go)
至此,一个web项目的开发基本就完成了。