W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
?Context
?指的是標準庫的?context.Context
?,是一個接口對象,常用于異步?IO
?控制以及上下文流程變量的傳遞。本文將要介紹的,是如何使用?Context
?傳遞流程間共享變量。
在Go的執(zhí)行流程中,特別是?HTTP/RPC
?執(zhí)行流程中,不存在”全局變量”獲取請求參數(shù)的方式,只有將上下文?Context
?變量傳遞到后續(xù)流程的方法中,而?Context
?上下文變量即包含了所有需要傳遞的共享變量。并且該?Context
?中的共享變量應當是事先約定的,并且往往存儲為對象指針形式。
通過?Context
?上下文共享變量非常簡單,以下我們通過一個項目中的示例來展示如何在實戰(zhàn)化項目中傳遞和使用通用的共享變量。
上下文對象中往往存儲一些需要共享的變量,這些變量通常使用結(jié)構(gòu)化的對象來存儲,以方便維護。例如,我們在?model
?定義一個上下文中的共享變量:
const (
// 上下文變量存儲鍵名,前后端系統(tǒng)共享
ContextKey = "ContextKey"
)
// 請求上下文結(jié)構(gòu)
type Context struct {
Session *ghttp.Session // 當前Session管理對象
User *ContextUser // 上下文用戶信息
Data g.Map // 自定KV變量,業(yè)務(wù)模塊根據(jù)需要設(shè)置,不固定
}
// 請求上下文中的用戶信息
type ContextUser struct {
Id uint // 用戶ID
Passport string // 用戶賬號
Nickname string // 用戶名稱
Avatar string // 用戶頭像
}
其中:
model.ContextKey
?常量表示存儲在?context.Context
?上下文變量中的鍵名,該鍵名用于從傳遞的?context.Context
?變量中存儲/獲取業(yè)務(wù)自定義的共享變量。
model.Context
?結(jié)構(gòu)體中的?Session
?表示當前請求的?Session
?對象,在?GoFrame
?框架中每個?HTTP
?請求對象中都會有一個空的?Session
?對象,該對象采用了懶初始化設(shè)計,只有在真正執(zhí)行讀寫操作時才會初始化。
model.Context
?結(jié)構(gòu)體中的?User
?表示當前登錄的用戶基本信息,只有在用戶登錄后才有數(shù)據(jù),否則是?nil
?。
model.Context
?結(jié)構(gòu)體中的?Data
?屬性用于存儲自定義的?KV
?變量,因此一般來說開發(fā)者無需再往?context.Context
?上下文變量中增加自定義的鍵值對,而是直接使用?model.Context
?對象的這個?Data
?屬性即可。詳見后續(xù)介紹。由于該上下文對象也是和業(yè)務(wù)邏輯相關(guān)的,因此我們需要通過?service
?對象將上下文變量封裝起來以方便其他模塊使用。
// 上下文管理服務(wù)
var Context = new(contextService)
type contextService struct{}
// 初始化上下文對象指針到上下文對象中,以便后續(xù)的請求流程中可以修改。
func (s *contextService) Init(r *ghttp.Request, customCtx *model.Context) {
r.SetCtxVar(model.ContextKey, customCtx)
}
// 獲得上下文變量,如果沒有設(shè)置,那么返回nil
func (s *contextService) Get(ctx context.Context) *model.Context {
value := ctx.Value(model.ContextKey)
if value == nil {
return nil
}
if localCtx, ok := value.(*model.Context); ok {
return localCtx
}
return nil
}
// 將上下文信息設(shè)置到上下文請求中,注意是完整覆蓋
func (s *contextService) SetUser(ctx context.Context, ctxUser *model.ContextUser) {
s.Get(ctx).User = ctxUser
}
上下文的變量必須在請求一開始便注入到請求流程中,以便于其他方法調(diào)用。在?HTTP
?請求中我們可以使用?GoFrame
?的中間件來實現(xiàn)。在?GRPC
?請求中我們也可以使用攔截器來實現(xiàn)。在?service
?層的?middleware
?管理對象中,我們可以這樣來定義:
// 自定義上下文對象
func (s *middlewareService) Ctx(r *ghttp.Request) {
// 初始化,務(wù)必最開始執(zhí)行
customCtx := &model.Context{
Session: r.Session,
Data: make(g.Map),
}
service.Context.Init(r, customCtx)
if userEntity := Session.GetUser(r.Context()); userEntity != nil {
customCtx.User = &model.ContextUser{
Id: userEntity.Id,
Passport: userEntity.Passport,
Nickname: userEntity.Nickname,
Avatar: userEntity.Avatar,
}
}
// 將自定義的上下文對象傳遞到模板變量中使用
r.Assigns(g.Map{
"Context": customCtx,
})
// 執(zhí)行下一步請求邏輯
r.Middleware.Next()
}
該中間件初始化了用戶執(zhí)行流程共享的對象,并且存儲到?context.Context
?變量中的對象是指針類型?*model.Context
?。這樣任何一個地方獲取到這個指針,既可以獲取到里面的數(shù)據(jù),也能夠直接修改里面的數(shù)據(jù)。
其中,如果?Session
?中存在用戶登錄后的存儲信息,那么也會將需要共享的用戶基本信息寫入到?*model.Context
?中。
約定俗成的,方法定義的第一個輸入?yún)?shù)往往預留給?context.Context
?類型參數(shù)使用,以便接受上下文變量,特別是?service
?層的方法。例如:
// 執(zhí)行用戶登錄
func (s *userService) Login(ctx context.Context, loginReq *define.UserServiceLoginReq) error {
...
}
// 查詢內(nèi)容列表
func (s *contentService) GetList(ctx context.Context, r *define.ContentServiceGetListReq) (*define.ContentServiceGetListRes, error) {
...
}
// 創(chuàng)建回復內(nèi)容
func (s *replyService) Create(ctx context.Context, r *define.ReplyServiceCreateReq) error {
...
}
此外,約定俗成的,方法的最后一個返回參數(shù)往往是?error
?類型。如果您確定此方法內(nèi)部永不會產(chǎn)生?error
?,那么可以忽略。
通過?service
?中封裝的以下方法,將?context.Context
?上下文變量傳遞進去即可。?context.Context
?上下文變量在?GoFrame
?框架的?HTTP
?請求中可以通過?r.Context()
?方法獲取,在?GRPC
?請求中,編譯生成的?pb
?文件中執(zhí)行方法的第一個參數(shù)即固定是?context.Context
?。
service.Context.Get(ctx)
通過以下方式設(shè)置/獲取自定義的?key-value
?鍵值對。
// 設(shè)置自定義鍵值對
service.Context.Get(ctx).Data[key] = value
...
// 獲取自定義鍵值對
service.Context.Get(ctx).Data[key]
上下文變量只傳遞必須的鏈路參數(shù)數(shù)據(jù),不要什么參數(shù)都往里面塞。特別是一些方法參數(shù)傳參的數(shù)據(jù),別往里面塞,而應當顯示傳遞方法參數(shù)。
上下文變量僅用作運行時臨時使用,不可持久化存儲長期使用。例如將?ctx
?序列化后存儲到數(shù)據(jù)庫,并再下一次請求中讀取出來反序列化使用是錯誤做法。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: