当前位置: 首页 > 科技观察

GoFrame如何优雅地共享变量?Context的使用

时间:2023-03-13 05:05:06 科技观察

前言昨天在合并代码的时候,发现了很多冲突。原因是同事在review项目,做linktracking,发现老项目中有很多传参方式。追踪。所以修改了这些方法和调用,造成了很多冲突,修复冲突也花了很长时间。所以:参数传递规范应该在项目开始的时候就确定好,一定要搞清楚如何使用Context!今天给大家介绍一下Context的使用:告诉大家什么是Context?如何使用?为什么使用Context和使用中的提示和注意事项。什么是语境?Context是指标准库的context.Context,是一个接口对象,常用于异步IO控制和上下文流程变量的传递。本文将介绍Context在业务流程中如何优雅的传递变量,以及为什么需要传递变量。为什么需要语境?在Go的执行过程中,尤其是在HTTP/RPC的执行过程中,没有办法通过“全局变量”来获取请求参数。只能通过上下文变量传递给后续执行过程的方法。如何使用?Context上下文变量包含所有需要传递的共享变量。而Context中的共享变量需要事先约定好,往往以对象指针的形式存储。通过Context上下文,共享变量非常简单。下面的示例将向您展示如何传递和使用公共共享变量。1.结构体定义Context对象中往往存放着一些需要共享的变量。这些变量通常存储在结构化对象中,以便于维护。比如我们在模型中定义了一个context中的共享变量:const(//context变量存储键名,前后端系统共享ContextKey="ContextKey")//请求上下文结构类型Contextstruct{Session*ghttp.Session//当前Session管理对象User*ContextUser//上下文用户信息Datag.Map//自定义KV变量,业务模块可根据需要设置,不固定}//请求上下文类型ContextUser中的用户信息struct{Iduint//用户IDPassportstring//用户账号Nicknamestring//用户名Avatarstring//用户头像}介绍model.ContextKey常量表示存储在context.Context上下文变量中的键名,用于从context.Context变量中存储/获取业务定义的共享变量。model.Context结构中的Session代表当前请求的Session对象。在GoFrame框架中,每个HTTP请求对象中都会有一个空的Session对象。该对象采用惰性初始化设计,只有在真正执行读写操作时才初始化。model.Context结构中的User表示当前登录用户的基本信息,只有用户登录后才有数据,否则为nil。model.Context结构中的数据用于存储自定义的KV变量,所以一般来说,开发者不需要在context.Context上下文变量中添加自定义的键值对,而是直接使用model的Data属性。上下文对象就足够了。2.逻辑封装由于上下文对象还与业务逻辑相关,所以我们需要通过服务对象对上下文变量进行封装,以方便其他模块的使用。//上下文管理服务varContext=new(contextService)typecontextServicestruct{}//初始化上下文对象指针指向上下文对象,以便在后续的请求过程中修改。func(s*contextService)Init(r*ghttp.Request,customCtx*model.Context){r.SetCtxVar(model.ContextKey,customCtx)}//获取上下文变量,如果没有设置则返回nilfunc(s*contextService)Get(ctxcontext.Context)*model.Context{value:=ctx.Value(model.ContextKey)ifvalue==nil{returnnil}iflocalCtx,ok:=value.(*model.Context);ok{returnlocalCtx}returnnil}//设置上下文信息到上下文请求,注意完整覆盖func(s*contextService)SetUser(ctxcontext.Context,ctxUser*model.ContextUser){s.Get(ctx).User=ctxUser}Tips在架构设计中,在什么场景下设置Context是非常关键的。上下文变量必须在请求开始时注入到请求过程中,以方便其他方法调用,所以在中间件中实现是一个非常优雅的选择。我们看下面的介绍:3.上下文变量注入我们可以使用GoFrame的中间件在HTTP请求中实现。我们也可以在GRPC请求中使用拦截器。在服务层的中间件管理对象中,我们可以这样定义://自定义上下文对象func(s*middlewareService)Ctx(r*ghttp.Request){//初始化,一定要先执行customCtx:=&model.Context{Session:r.Session,Data:make(g.Map),}service.Context.Init(r,customCtx)ifuserEntity:=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,})//执行下一个请求逻辑r.Middleware.Next()}这个中间件初始化用户共享的对象来执行流程,并存储在context.Context变量中对象是指针类型*model.Context。这样做的好处是,如果在任何地方获取这个指针,不仅可以获取到里面的数据,还可以直接修改里面的数据。TIPS如果Session中有用户登录后存储的信息,那么需要共享的用户基础信息也会写入*model.Context中。4.上下文变量使用方法定义方法定义的第一个入参往往预留context.Context类型的参数,以接受上下文变量,尤其是服务层的方法。例如://执行用户登录func(s*userService)Login(ctxcontext.Context,loginReq*define.UserServiceLoginReq)error{...}//查询内容列表func(s*contentService)GetList(ctxcontext.Context,r*define.ContentServiceGetListReq)(*define.ContentServiceGetListRes,error){...}//创建回复内容func(s*replyService)Create(ctxcontext.Context,r*define.ReplyServiceCreateReq)error{...}TIPS另一个好习惯是:方法的最后一个返回参数往往是error类型的。如果你确定错误永远不会在方法内部产生,你可以忽略它。通过服务中封装的如下方法获取Context对象,可以传入context.Context上下文变量,通过GoFrame的HTTP请求中的r.Context()方法获取context.Context上下文变量框架。在GRPC请求中,编译后的pb文件中执行方法的第一个参数始终是context.Context。service.Context.Get(ctx)CustomKey-Value我们可以通过以下方式设置/获取自定义key-value键值对。//Setacustomkey-valuepairservice.Context.Get(ctx).Data[key]=value//Getacustomkey-valuepairservice.Context.Get(ctx).Data[key]5.注意给问题的上下文变量只传递必要的链接参数数据,不要把所有的参数都塞进去。特别是有些方法参数和参数数据一定不能塞进context中,而应该显式传递方法参数。上下文变量仅在运行时临时使用,不能用于长期持久存储。综上所述,本文详细介绍了GoFrame上下文对象Context的知识点:Context的作用:在业务流程中共享变量。Context的结构定义,逻辑封装,如何在中间件中注入,如何通过Context进行取值,如何自定义Context的key-value,项目开发中的注意事项。欢迎关注StarGoFrame:https://github.com/gogf/gf本文转载自微信公众号《程序员升级打怪之旅》,作者“王中扬围棋”,可通过以下二维码关注.转载本文请联系《程序员升级打怪之旅》公众号。