字節跳動 Golang 微服務框架 Hertz 集成 Session
今天我們將探討如何在字節跳動的 Golang 微服務框架 Hertz 中集成 session 功能。
通過實踐,我們會逐步了解如何在項目中完成從環境搭建到接口調試的流程。
一、Hertz 項目目錄結構
以下是 Hertz 項目的基本目錄結構,展示了如何集成 session:
├── Makefile # 定義項目的自動化任務腳本
├── README.md
├── biz
│ ├── dal
│ │ ├── init.go
│ │ └── mysql
│ │ ├── init.go # 連接信息
│ │ └── user.go # 包含對用戶數據在 MySQL 中的操作
│ ├── handler
│ │ ├── ping.go
│ │ └── user
│ │ └── user_service.go
│ ├── model
│ │ └── user
│ │ └── user.go
│ ├── mw
│ │ ├── csrf.go # 跨域
│ │ └── session.go
│ └── router
│ ├── register.go
│ └── user
│ ├── middleware.go
│ └── user.go
├── docker-compose.yml # 定義和運行多個 Docker 容器的配置文件
├── go.mod
├── go.sum
├── idl
│ └── user.thrift
├── images
│ ├── index-page.png
│ ├── login-page.png
│ └── register-page.png
├── main.go
├── pkg
│ ├── consts
│ │ └── consts.go
│ ├── render
│ │ └── render.go
│ └── utils
│ └── utils.go
├── router.go
├── router_gen.go
二、運行項目
Step 1:啟動 MySQL 容器
首先,使用 Docker 啟動 MySQL 容器:
cd bizdemo/hertz_session && docker-compose up
運行后,您會看到如下輸出:
docker運行mysql
Step 2:編譯并運行項目
在終端運行以下命令來啟動項目:
cd bizdemo/hertz_session && go run main.go
看到以下日志就說明啟動成功了:
2024/11/12 23:51:05.802956 engine.go:396: [Info] HERTZ: Using network library=netpoll
2024/11/12 23:51:05.807942 transport.go:115: [Info] HERTZ: HTTP server listening on address=[::]:8888
項目啟動
三、API 接口調試
1. /register 用戶注冊接口
請求 URL: http://localhost:8888/register.html
請求參數:
{
"username": "hertz_session",
"email": "1122@qq.com",
"password": "password"
}
響應結果:
圖片
注冊成功后,在數據庫中可以看到新用戶記錄:
數據庫查詢
2. /login 用戶登錄接口
請求 URL: http://localhost:8888/login.html
輸入用戶名和密碼,成功后返回登錄結果:
登錄成功
3. /ping 接口
請求 URL: http://localhost:8888/ping
響應結果:
{
"message": "pong"
}
接口響應
四、代碼解析
4.1 根據 user.thrift 生成接口代碼
以下是定義的 UserService 服務接口代碼:
namespace go user
struct BaseResp {
1: i64 code
2: string message
}
struct RegisterRequest {
1: string username (api.form="username", api.vd="(len($) > 0 && len($) < 128); msg:'Illegal format'")
2: string password (api.form="password", api.vd="(len($) > 0 && len($) < 128); msg:'Illegal format'")
3: string email (api.form="email", api.vd="(len($) > 0 && len($) < 128) && email($); msg:'Illegal format'")
}
struct RegisterResponse {
1: BaseResp baseresp
}
service UserService {
RegisterResponse register(1: RegisterRequest req) (api.post="/register")
LoginResponse login(1: LoginRequest req) (api.post="/login")
}
4.2 實現用戶注冊接口
在 router.go 中配置路由:
func Register(r *server.Hertz) {
root := r.Group("/", rootMw()...)
root.POST("/login", append(_loginMw(), user.Login)...)
root.POST("/register", append(_registerMw(), user.Register)...)
}
在 user_service.go 中實現 Register 處理函數:
func Register(ctx context.Context, c *app.RequestContext) {
// 接收注冊參數并驗證
var registerStruct struct {
Username string `form:"username" json:"username"`
Email string `form:"email" json:"email"`
Password string `form:"password" json:"password"`
}
// 綁定參數并驗證
if err := c.BindAndValidate(?isterStruct); err != nil {
c.JSON(http.StatusOK, utils.H{"message": err.Error(), "code": http.StatusBadRequest})
return
}
// 檢查用戶名和郵箱是否存在
users, err := mysql.FindUserByNameOrEmail(registerStruct.Username, registerStruct.Email)
if err != nil || len(users) != 0 {
c.JSON(http.StatusOK, utils.H{"message": "user already exists", "code": http.StatusBadRequest})
return
}
// 創建用戶
err = mysql.CreateUsers([]*model.User{{
UserName: registerStruct.Username,
Email: registerStruct.Email,
Password: utils.MD5(registerStruct.Password),
}})
if err != nil {
c.JSON(http.StatusOK, utils.H{"message": err.Error(), "code": http.StatusBadRequest})
return
}
c.JSON(http.StatusOK, utils.H{"message": "success", "code": http.StatusOK})
}
4.3 登錄實現
在 user_service.go 中實現 Login 函數:
func Login(_ context.Context, c *app.RequestContext) {
var req user.LoginRequest
if err := c.BindAndValidate(&req); err != nil {
c.HTML(http.StatusOK, "login.html", hutils.H{"message": err.Error()})
return
}
users, err := mysql.CheckUser(req.Username, utils.MD5(req.Password))
if err != nil || len(users) == 0 {
c.HTML(http.StatusOK, "login.html", hutils.H{"message": consts.LoginErr})
return
}
session := sessions.Default(c)
session.Set(consts.Username, req.Username)
_ = session.Save()
c.Redirect(http.StatusMovedPermanently, []byte("/index.html"))
}
總結:
通過詳細的代碼示例,逐步實現了基于 Hertz 框架的用戶會話管理功能。我們配置了目錄結構、引入了數據庫存儲用戶數據,并提供了注冊和登錄接口,演示了完整的會話創建與管理流程。
通過 session 實現的用戶認證邏輯,確保了登錄用戶的持續訪問控制,提升了項目的安全性和用戶體驗。這一套流程幫助開發者更深入理解 Hertz 框架在實際場景中的應用。