WEB服务开发:https://goframe.org/pages/viewpage.action?pageId=1114405
开始使用
先来一个Hello World:
1 | package main |
其他:平滑重启,HTTPS支持
路由注册
路由规则
1 | func main() { |
BindHandler的原型:
1 | func (s *Server) BindHandler(pattern string, handler HandlerFunc) error |
其中HTTPMethod(支持的Method:GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE)和@域名为非必需参数
1 | // 该路由规则仅会在GET请求及localhost域名下有效 |
优先级控制
优先级控制按照深度优先策略,最主要的几点因素:
1 | 层级越深的规则优先级越高; |
路由注册
注册方式 | 使用难度 | 安全系数 | 执行性能 | 内存消耗 |
---|---|---|---|---|
回调函数注册 | 高 | 低 | 高 | 低 |
执行对象注册 | 中 | 中 | 中 | 中 |
控制器方式注册 | 低 | 高 | 低 | 高 |
比较指标说明:
- 使用难度:主要指对于执行流程以及数据管理维护的复杂度;
- 安全系数:主要指在异步多协程下的数据安全管理维护;
- 执行性能:执行性能,相对比较的结果;
- 内存消耗:内存消耗,相对比较的结果;
注册方法
1 | func (s *Server) BindHandler(pattern string, handler HandlerFunc) error |
域名路由注册方法(略)
注册方式
函数注册(略)
对象注册
1 |
|
对象方法注册
假如控制器中有若干公开方法,但是我只想注册其中几个,其余的方法我不想对外公开
我们可以通过BindObject传递第三个非必需参数替换实现,参数支持传入多个方法名称,多个名称以英文,号分隔(方法名称参数区分大小写)。
1 | s.BindObject("/object", c, "Show") |
绑定路由方法
我们可以通过BindObjectMethod方法绑定指定的路由到指定的方法执行(方法名称参数区分大小写)。
BindObjectMethod和BindObject的区别: BindObjectMethod将对象中的指定方法与指定路由规则进行绑定,第三个method参数只能指定一个方法名称; BindObject注册时,所有的路由都是对象方法名称按照规则生成的,第三个methods参数可以指定多个注册的方法名称。
RESTful对象注册
构造方法Init与析构方法Shut
对象中的Init和Shut是两个在HTTP请求流程中被Server自动调用的特殊方法(类似构造函数和析构函数的作用)。
分组路由
层级注册
中间件/拦截器
前置中间件
后置中间件
请求输入
提交方式
GF框架下,有以下几种提交类型:
Router: 路由参数,来源于路由规则匹配。
Query: URL中的Query String参数解析,如:http://127.0.0.1/index?id=1&name=john 中的id=1&name=john。
Form: 表单提交参数,最常见的提交方式,提交的Content-Type往往为:application/x-www-form-urlencoded、multipart/form-data、multipart/mixed。
Body: 原始提交内容,从Body中获取并解析得到的参数,JSON/XML请求往往使用这种方式提交。
Custom: 自定义参数,往往在服务端的中间件、服务函数中通过SetParam/GetParam方法管理。
参数类型
获取的参数方法可以对指定键名的数据进行自动类型转换,例如:http://127.0.0.1:8199/?amount=19.66,通过GetQueryString将会返回19.66的字符串类型,GetQueryFloat32/GetQueryFloat64将会分别返回float32和float64类型的数值19.66
如果有更多参数类型转换的需求,可以使用Get*Var参数获取方法,获得*gvar.Var变量再进行相应类型转换。例如,假如我们要获取一个int8类型的参数,我们可以这样GetVar(“id”).Int8()。
参数优先级
Get*及GetRequset*方法:Router < Query < Body < Form < Custom,也就是说自定义参数的优先级最高,其次是Form表单参数,再次是Body提交参数,以此类推。例如,Query和Form中都提交了同样名称的参数id,参数值分别为1和2,那么Get(“id”)/GetForm(“id”)将会返回2,而GetQuery(“id”)将会返回1。
GetQuery*方法:Query > Body,也就是说query string的参数将会覆盖Body中提交的同名参数。例如,Query和Body中都提交了同样名称的参数id,参数值分别为1和2,那么Get(“id”)将会返回2,而GetQuery(“id”)将会返回1。
GetForm*方法:由于该类型的方法仅用于获取Form表单参数,因此没什么优先级的差别。
数据返回
Write*方法用于数据的输出,可为任意的数据格式,内部通过断言对参数做自动分析。
Write*Exit方法用于数据输出后退出当前服务方法,可用于替代return返回方法。
WriteJson*/WriteXml方法用于特定数据格式的输出,这是为开发者提供的简便方法。
WriteTpl*方法用于模板输出,解析并输出模板文件,也可以直接解析并输出给定的模板内容。
ParseTpl*方法用于模板解析,解析模板文件或者模板内容,返回解析后的内容。
缓冲控制
Response输出采用了缓冲控制,输出的内容预先写入到一块缓冲区,等待服务方法执行完毕后才真正地输出到客户端。该特性在提高执行效率同时为输出内容的控制提供了更高的灵活性。
举个例子:
我们通过后置中间件统一对返回的数据做处理,如果服务方法产生了异常,那么不能将敏感错误信息推送到客户端,而统一设置错误提示信息。
1 |
|
模板解析
Response支持模板文件/内容解析输出,或者模板文件/内容解析返回。与直接使用模板对象解析模板功能不同的是,Response的解析支持一些请求相关的内置变量。模板解析包含以下方法:
WriteTpl*方法用于模板输出,解析并输出模板文件,也可以直接解析并输出给定的模板内容。
ParseTpl*方法用于模板解析,解析模板文件或者模板内容,返回解析后的内容。
1 | s.BindHandler("/", func(r *ghttp.Request){ |
JSON/XML
WriteJson* 方法用于返回JSON数据格式,参数为任意类型,可以为string、map、struct等等。返回的Content-Type为application/json。
WriteXml* 方法用于返回XML数据格式,参数为任意类型,可以为string、map、struct等等。返回的Content-Type为application/xml。
1 | group.ALL("/json", func(r *ghttp.Request) { |
Cookie
1 | s.BindHandler("/cookie", func(r *ghttp.Request) { |
Session
任何时候都可以通过ghttp.Request获取Session对象,因为Cookie和Session都是和请求会话相关,因此都属于Request的成员对象,并对外公开。GF框架的Session默认过期时间是24小时。
SessionId默认通过Cookie来传递,并且也支持客户端通过Header传递SessionId,SessionId的识别名称可以通过ghttp.Server的SetSessionIdName进行修改。
Session的操作是支持并发安全的,这也是框架在对Session的设计上不采用直接以map的形式操作数据的原因。在HTTP请求流程中,我们可以通过ghttp.Request对象来获取Session对象,并执行相应的数据操作。
ghttp.Server中的SessionId使用的是客户端的 RemoteAddr + Header 请求信息通过guid模块来生成的,保证随机及唯一性:https://github.com/gogf/ gf/blob/master/net/ghttp/ghttp_request.go
Session-文件存储
Session-内存存储
Session-Redis存储
1 | s.SetConfigWithMap(g.Map{ |
服务配置(略)
异常处理
我们这里使用一个全局的后置中间件来捕获异常,当异常产生后,捕获并写入到指定的日志文件中,返回固定友好的提示信息,避免敏感的报错信息暴露给调用端。
1 |
|
HTTPClient(略)
gf-jwt说明
1 | // 重新定义GfJWTMiddleware |
整体调用链如下
有了上图就大致清楚,我们希望校验token时,做某些操作(比如查询刷新菜单权限等),就是在IdentityHandler中修改了。
用户名密码登录时,记录登录日志,在Authenticator里更合适。