Goframe框架细节

一、OmitEmpty空值过滤

空值会影响于写入/更新操作方法,如Insert, Replace, Update, Save操作。

当 map/struct 中存在空值如 nil,"",0 时,默认情况下,gdb将会将其当做正常的输入参数,因此这些参数也会被更新到数据表。

针对空值情况,我们可以通过OmitEmpty方法来过滤掉这些空值。

示例:db.Table("user").OmitEmpty().Data(user).Insert()

二、数据返回-模板解析

Response支持模板文件/内容解析输出,或者模板文件/内容解析返回。与直接使用模板对象解析模板功能不同的是,Response的解析支持一些请求相关的内置变量。模板解析包含以下方法:

WriteTpl*方法用于模板输出,解析并输出模板文件,也可以直接解析并输出给定的模板内容。

ParseTpl*方法用于模板解析,解析模板文件或者模板内容,返回解析后的内容。

解析模板时组件底层会自动通过Request对象获取当前链路的Context上下文变量并传递给模板引擎,因此开发者不用显示给模板引擎传递Context上下文变量。

内置变量:

            1、Config:访问默认的配置管理(config.toml)对象配置项。

                                使用方式:{{.Config.配置项}}

            2、Cookie:访问当前请求的Cookie对象参数值。

                                使用方式:{{.Cookie.键名}}


            3、Session:访问当前请求的Session对象参数值。

                                 使用方式:{{.Session.键名}}

            4、Query:访问当前Query String中的请求参数值。

                                使用方式:{{.Query.键名}}

            5、Form:访问当前表单请求参数值。

                                使用方式:{{.Form.键名}}

            6、Request:访问当前请求参数值(不区分参数提交方式)。

                                使用方式:{{.Request.键名}}

三、请求输入-Context

请求流程往往会在上下文中共享一些自定义设置的变量,例如在请求开始之前通过中间件设置一些变量,随后在路由服务方法中可以获取该变量并相应对一些处理。这种需求非常常见。在GoFrame框架中,我们推荐使用Context上下文对象来处理流程共享的上下文变量,甚至将该对象进一步传递到依赖的各个模块方法中。该Context对象类型实现了标准库的context.Context接口,该接口往往会作为模块间调用方法的第一个参数,该接口参数也是Golang官方推荐的在模块间传递上下文变量的推荐方式。

方法列表:

func(r*Request)GetCtx()context.Context

func(r*Request)SetCtx(ctx context.Context)

func(r*Request)GetCtxVar(keyinterface{},def...interface{})*gvar.Var

func(r*Request)SetCtxVar(keyinterface{},valueinterface{})

简要说明:

GetCtx方法用于获取当前的context.Context对象,作用同Context方法。

SetCtx方法用于设置自定义的context.Context上下文对象。

GetCtxVar方法用于获取上下文变量,并可给定当该变量不存在时的默认值。

SetCtxVar方法用于设置上下文变量。

可以通过SetCtxVar和GetCtxVar来设置和获取自定义的变量,该变量生命周期仅限于当前请求流程。

四、HTTPClient-文件上传

在服务端通过Request对象获取上传文件:

funcUpload(r*ghttp.Request){

files:=r.GetUploadFiles("upload-file")

names,err:=files.Save("/tmp/")

iferr!=nil{

r.Response.WriteExit(err)

}

r.Response.WriteExit("upload successfully: ",names)}

关键代码说明

我们在服务端可以通过r.GetUploadFiles方法获得上传的所有文件对象,也可以通过r.GetUploadFile获取单个上传的文件对象。

在r.GetUploadFiles("upload-file")中的参数"upload-file"为本示例中客户端上传时的表单文件域名称,开发者可以根据前后端约定在客户端中定义,以方便服务端接收表单文件域参数。

通过files.Save可以将上传的多个文件方便地保存到指定的目录下,并返回保存成功的文件名。如果是批量保存,只要任意一个文件保存失败,都将会立即返回错误。此外,Save方法的第二个参数支持随机自动命名上传文件。

通过group.POST("/", Upload)注册的路由仅支持POST方式访问。

文件上传参数格式使用了 参数名=@file:文件路径 ,HTTP客户端将会自动解析文件路径对应的文件内容并读取提交给服务端。原本复杂的文件上传操作被gf进行了封装处理,用户只需要使用 @file:+文件路径 来构成参数值即可。其中,文件路径请使用本地文件绝对路径。

多个文件上传提交参数格式为参数名=@file:xxx&参数名=@file:xxx...,也可以使用参数名[]=@file:xxx&参数名[]=@file:xxx...的形式。

五、Session

任何时候都可以通过ghttp.Request获取Session对象,因为Cookie和Session都是和请求会话相关,因此都属于Request的成员对象,并对外公开。GoFrame框架的Session默认过期时间是24小时。

SessionId默认通过Cookie来传递,并且也支持客户端通过Header传递SessionId,SessionId的识别名称可以通过ghttp.Server的SetSessionIdName进行修改。Session的操作是支持并发安全的,这也是框架在对Session的设计上不采用直接以map的形式操作数据的原因。在HTTP请求流程中,我们可以通过ghttp.Request对象来获取Session对象,并执行相应的数据操作。

gsession模块

Session的管理功能由独立的gsession模块实现,并已完美整合到了ghttp.Server中。由于该模块是解耦独立的,因此可以应用到更多不同的场景中,例如:TCP通信、gRPC接口服务等等。在gsession模块中有比较重要的三个对象/接口:

gsession.Manager:管理Session对象、Storage持久化存储对象、以及过期时间控制。

gsession.Session:单个Session会话管理对象,用于Session参数的增删查改等数据管理操作。

gsession.Storage:这是一个接口定义,用于Session对象的持久化存储、数据写入/读取、存活更新等操作,开发者可基于该接口实现自定义的持久化存储特性。

Session的初始化

以常见的HTTP请求为例。ghttp.Request中的Session对象采用了"懒初始化(LazyInitialization)"设计方式,默认在Request中有一个Session属性对象,但是并未初始化(一个空对象),只有在使用Session属性对象的方法时才会真正执行初始化。这样的设计既保障了未使用Session特性的请求执行性能,也保证了组件使用的易用性。

Session的销毁/注销

用户Session不再使用,例如用户注销登录状态,需要从存储中硬删除,那么可以调用RemoveAll方法。

在默认情况下,ghttp.Server的Session存储使用了内存+文件的方式,使用StorageFile对象实现。具体原理为:

Session的数据操作完全基于内存;

使用gcache进程缓存模块控制数据过期;

使用文件存储持久化存储管理Session数据;

当且仅有当Session被标记为dirty时(数据有更新)才会执行Session序列化并执行文件持久化存储;

当且仅当内存中的Session不存在时,才会从文件存储中反序列化恢复Session数据到内存中,降低IO调用;

序列化/反序列化使用的是标准库的json.Marshal/UnMarshal方法;

六、日志管理

使用配置文件的方式来管理服务配置以及日志配置。 一个参考的日志配置内容示例(以yaml格式为例):

logger:

path:"/var/log/"# 日志文件路径。默认为空,表示关闭,仅输出到终端

file:"{Y-m-d}.log"# 日志文件格式。默认为"{Y-m-d}.log"

prefix:""# 日志内容输出前缀。默认为空

level:"all"# 日志输出级别

ctxKeys:[]# 自定义Context上下文变量名称,自动打印Context的变量到日志中。默认为空

header:true# 是否打印日志的头信息。默认true

stdout:true# 日志是否同时输出到终端。默认true

rotateSize:0# 按照日志文件大小对文件进行滚动切分。默认为0,表示关闭滚动切分特性

rotateExpire:0# 按照日志文件时间间隔对文件滚动切分。默认为0,表示关闭滚动切分特性

rotateBackupLimit:0# 按照切分的文件数量清理切分文件,当滚动切分特性开启时有效。默认为0,表示不备份,切分则删除

rotateBackupExpire:0# 按照切分的文件有效期清理切分文件,当滚动切分特性开启时有效。默认为0,表示不备份,切分则删除

rotateBackupCompress:0# 滚动切分文件的压缩比(0-9)。默认为0,表示不压缩

rotateCheckInterval:"1h"# 滚动切分的时间检测间隔,一般不需要设置。默认为1小时

stdoutColorDisabled:false# 关闭终端的颜色打印。默认开启

writerColorEnable:false# 日志文件是否带上颜色。默认false,表示不带

七、自定义状态码处理

我们可以对WebServer指定的状态码进行自定义处理,例如针对常见的404/403/500等错误,我们可以展示自定义的错误信息、页面内容,或者跳转到一个特定的页面。

我们可以使用BindStatusHandler或者BindStatusHandlerMap来实现针对指定的状态码进行自定义的回调函数处理,并且该特性也支持针对特定的域名绑定。

我们来看几个简单的示例:

s.BindStatusHandler(404,func(r*ghttp.Request){

r.Response.Writeln("This is customized 404 page")

})

执行后,当我们访问没有绑定的路由页面,可以看到,页面显示了我们期望的返回结果:This is customized 404 page。

s.BindStatusHandler(404,func(r*ghttp.Request){

r.Response.RedirectTo("/status/404")

})

执行后,我们手动通过浏览器访问一个不存在的页面,可以看到,页面被引导跳转到了 http://127.0.0.1:8199/status/404 页面,并且可以看到页面返回内容:woops, status 404 found

八、数据校验-校验规则

required

格式: required

说明:必需参数,除了支持常见的字符串,也支持Slice/Map类型。

示例:姓名字段Name为必需参数且不能为空。

typeBizReqstruct{

IDuint`v:"required"`

Namestring`v:"required"`

}

email

格式:email

说明:EMAIL邮箱地址

phone

格式:phone

说明:手机号

phone-loose

格式: phone

说明:宽松的手机号验证,只要满足 13、14、15、16、17、18、19开头的11位数字都可以通过验证。

passport

格式: passport

说明:通用帐号规则(字母开头,只能包含字母、数字和下划线,长度在6~18之间)。

password

格式: password

说明:通用密码规则(任意可见字符,长度在6~18之间)。

json

格式: json

说明:判断数据格式为JSON

same

格式: same:field

说明:参数值必需与field参数的值相同

示例:在用户注册时,提交密码Password和确认密码Password2必须相等(服务端校验)。

typeBizReqstruct{

Namestring`v:"required"`

Passwordstring`v:"required|same:Password2"`

Password2string`v:"required"`

}

regex

格式: regex:pattern

说明:参数值应当满足正则匹配规则pattern

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容