今天看到Golang排名到前30名了,看来关注的人越来越多了,接下来几天详细介绍Golang一个web开发框架GWK。

现在博客园支持markdown格式发布文章么?后台的编辑器不太好用嘛。

GWK

简介

gwk(GO Web Server Kit)是GO语言的Web Server开发框架,简单易用,扩展性好,而且兼容Go App Engine。

安装

gwk只支持GO 1.1+版本,安装GO 1.1后,运行下面的命令即可。

  1. go get github.com/sdming/wk

gwk依赖kiss和mcache两个package,如果没有自动安装成功的话,可以单独运行下面的命令安装:

  1. go get github.com/sdming/kiss
  2. go get github.com/sdming/mcache

示例

gwk的文档比较简单,写的不是很详细,自带的demo写的比较全,主要的功能点都涉及到了。

另外Google App Engine上有一个展示gwk demo的网站,是用gwk框架搭建的,也是一个了解gwk的地方。网址是[http://gwk-demo.appspot.com]。 需要注意的是,这个demo网站的示例数据是放在内存的,多用户访问时会互相影响 。另外App Engine会自动管理服务实例以及会根据访问情况自动关闭或启动服务,示例数据也会受到影响。

启动服务

gwk不像revel那样是一个Web Server框架,需要自己写代码来启动gwk的服务。最简单的方式如下。

  1. server, err := wk.NewDefaultServer()
  2. if err != nil {
  3. fmt.Println("NewDefaultServer error", err)
  4. return
  5. }
  6. server.RouteTable.Get("/data/top/{count}").To(...)
  7. server.Start()

基本步骤就是:

  • 创建HttpServer示例
  • 注册路由
  • 调用Start方法监听Http端口

接下来详细介绍gwk各个功能模块的用法,先从路由开始。

路由

gwk用RouteTable来存储注册的路由,RouteTable的定义如下:

  1. type RouteTable struct {
  2. Routes []*RouteRule
  3. }

当gwk接收到http请求时,按照顺序遍历RouteRule直到找到匹配的Route,如果没有找到则返回404。

RouteRule的定义如下:

  1. type RouteRule struct {
  2. // Methos is http method of request
  3. Method string
  4. // Pattern is path pattern
  5. Pattern string
  6. // Handler process request
  7. Handler Handler
  8. }

Method是Http method,比如:GET, POST, PUT, DELETE,*代表匹配所有的http method。
Pattern是URL匹配的模式,具体的格式下面再讲。
Handler是用来处理请求的代码,是一个接口,定义如下:

  1. type Handler interface {
  2. Execute(ctx *HttpContext)
  3. }

gwk提供了若干方法来注册路由。一个最简单的方法是路由到一个func (*wk.HttpContext) (wk.HttpResult, error) 类型的函数,比如:

  1. // url: /data/top/10
  2. server.RouteTable.Get("/data/top/{count}").To(DataTopHandle)

上面的代码将GET /data/top/10这样的request path注册到一个func (*wk.HttpContext) (wk.HttpResult, error)类型的函数,例子中DataTopHandle的定义如下:

  1. func DataTopHandle(ctx *wk.HttpContext) (result wk.HttpResult, err error) {
  2. if count, ok := ctx.RouteData.Int("count"); !ok {
  3. err = errors.New("parameter invalid:" + "count")
  4. } else {
  5. data := DataTop(count)
  6. result = wk.Json(data)
  7. }
  8. return
  9. }

例子中HttpContext是这次http请求的一个封装,HttpResult是这次请求返回的数据,详细的定义在下面会介绍。

通过RouteData可以获得在request path中匹配到的参数,比如上面的{count}参数,RouteData的定义如下:

  1. type RouteData map[string]string

RouteData提供了若干方法来简化Route参数的读取。

读取整数

  1. func (r RouteData) Int(name string) (int, bool)

读取整数,如果参数不存在或者不是有效的整数格式则返回一个缺省值

  1. func (r RouteData) IntOr(name string, v int) int

类似的方法还有

  1. BoolBoolOrFloatFloatOrStrStrOr

asp.net mvc 的开发人员应该会对"/data/top/{count}"这样的路由规则写法比较熟悉,gwk支持两种路由规则的写法。

  • 正则表达式

    1. ^/user/?(?P<action>[[:alnum:]]+)?/?(?P<arg>[[:alnum:]]+)?/?

    匹配类似/user/view/1这样的request path

  • 正则表达式写起来比较麻烦,而且执行速度慢,你也可以用类似asp.net mvc的写法,比如

    1. "/data/top/{count}"
    2. "/query/{year}-{month}-{day}"
    3. "/basic#/{action}/{id}"
    4. "/data/int/{p0}?"
    5. "/data/range/{p0}-{p1}"

    规则很简单,前缀匹配requets path,{}匹配的内容会被提取为参数。其中两个特殊字符需要介绍一下。

    • "#"字符代表精确匹配#之前的内容,#之后的为可选匹配,比如/basic和/basic/add,/basic/delete/1都匹配上面规则。
    • "?"字符代表匹配request path的结束,比如/data/int/1匹配上面的规则,/data/int/1/foo就不符合上面的规则。

gwk还提供了其他的方式来注册路由。

通过ToFunc方法注册路由到一个普通函数,比如

  1. // url: /data/int/1
  2. server.RouteTable.Get("/data/int/{p0}?").ToFunc(model.DataByInt)
  3. func DataByInt(i int) *Data {
  4. if i < 0 {
  5. i = 0
  6. }
  7. return newData(i)
  8. }

再比如

  1. // url: /data/range/1-9
  2. server.RouteTable.Get("/data/range/{p0}-{p1}").ToFunc(model.DataByIntRange)
  3. func DataByIntRange(start, end int) []*Data {
  4. data := make([]*Data, 2)
  5. data[0] = newData(start)
  6. data[1] = newData(end)
  7. return data
  8. }

因为GO的反射不能获得函数参数的名字,所以这里用p0,p1,p2...来代表函数的第0,1,2...个参数。

gwk会根据http请求中accept的内容来自动决定返回数据的格式,上面的例子中Data定义如下:

  1. type Data struct {
  2. Str string
  3. Uint uint64
  4. Int int
  5. Float float32
  6. Byte byte
  7. }

如果Request的Accept中包含字符串"xml",则结果序列化为xml格式,如果包含"jsonp",则结果序列化为jsonp,如果包含json,则序列化为json格式,详细信息可以参考下面的"格式化"一节

除了上面的p0,p1,p2指定参数的方式,可以用BindByNames按照名字来绑定函数的参数,比如:

  1. // url: /data/name/1
  2. server.RouteTable.Get("/data/name/{id}").ToFunc(model.DataByInt).
  3. BindByNames("id")

上面的代码告诉gwk,路由参数"id"是函数DataByInt的第一个参数。

  1. // url: /data/namerange/1-9
  2. server.RouteTable.Get("/data/namerange/{start}-{end}").ToFunc(model.DataByIntRange).
  3. BindByNames("start", "end")

上面的代码告诉gwk,路由参数"start","end"是函数DataByIntRange的第一个和第二个参数。

也可以绑定到querypath或者form中的参数,比如:

  1. // url: /data/namerange/?start=1&end=9
  2. server.RouteTable.Get("/data/namerange/").ToFunc(model.DataByIntRange).
  3. BindByNames("start", "end")

再比如:

  1. // url: get /data/set?str=string&uint=1024&int=32&float=3.14&byte=64
  2. server.RouteTable.Get("/data/set?").ToFunc(model.DataSet).
  3. BindByNames("str", "uint", "int", "float", "byte")
  4. func DataSet(s string, u uint64, i int, f float32, b byte) *Data {
  5. return &Data{
  6. Str: s,
  7. Uint: u,
  8. Int: i,
  9. Float: f,
  10. Byte: b,
  11. }
  12. }

如果参数比较多,一个合适的方法是将参数定义为一个struct,然后调用BindToStruct来绑定参数。比如:

  1. // url: post /data/post?
  2. // form:{"str": {"string"}, "uint": {"1024"}, "int": {"32"}, "float": {"1.1"}, "byte": {"64"}}
  3. server.RouteTable.Post("/data/post?").ToFunc(model.DataPost).BindToStruct()
  4. func DataPost(data Data) string {
  5. return data.String()
  6. }
  7. // url: post /data/postptr?
  8. // form:{"str": {"string"}, "uint": {"1024"}, "int": {"32"}, "float": {"1.1"}, "byte": {"64"}}
  9. server.RouteTable.Post("/data/postptr?").ToFunc(model.DataPostPtr).BindToStruct()
  10. func DataPostPtr(data *Data) string {
  11. return data.String()
  12. }

如果觉得每个函数定义一个参数对象比较麻烦,也可以用匿名对象:

  1. server.RouteTable.Get("/data/anonymous?").ToFunc(DataAnonymous).BindToStruct()
  2. func DataAnonymous(data struct {
  3. Str string
  4. Uint uint64
  5. Int int
  6. Float float32
  7. Byte byte
  8. },) string {
  9. return fmt.Sprintln(data)
  10. }

更方便的路由方式是注册一个到controller的路由,在controller一节会介绍。

格式化

gwk会根据http请求中accept的内容来自动决定返回数据的格式,如果Request的Accept中包含字符串"xml",则结果序列化为xml格式,如果包含"jsonp",则结果序列化为jsonp,如果包含json,则序列化为json格式。具体的例子可以看http://gwk-demo.appspot.com/doc/routedemo

你也可以通过设置Formatter来指定输出的格式,Formatter的定义如下

  1. type FormatFunc func(*HttpContext, interface{}) (HttpResult, bool)

gwk默认支持两种序列化方式,xml和json。比如:

  1. // url: /data/int/1/xml
  2. server.RouteTable.Get("/data/int/{p0}/xml").ToFunc(model.DataByInt).ReturnXml()
  3. // url: /data/int/1/json
  4. server.RouteTable.Get("/data/int/{p0}/json").ToFunc(model.DataByInt).ReturnJson()

ReturnXml指定DataByInt的返回值格式化为xml,ReturnJson指定DataByInt的返回值格式化为json。

你也可以自定义序列化的方式,比如:

  1. // url: /data/int/1/kson
  2. server.RouteTable.Get("/data/int/{p0}/kson").ToFunc(model.DataByInt).Return(formatKson)
  3. func formatKson(ctx *wk.HttpContext, x interface{}) (wk.HttpResult, bool) {
  4. b, _ := kson.Marshal(x)
  5. return wk.Content(string(b), "text/plain"), true
  6. }

kson格式是gwk的配置文件采用的格式,后文会详细介绍,上面的代码返回的数据如下:

  1. {
  2. Str:string:1
  3. Uint:100
  4. Int:10
  5. Float:1.1
  6. Byte:1
  7. }

gwk还提供了注册全局FormatFunc的地方:

  1. type FormatList []FormatFunc
  2. var Formatters FormatList

你可以通过增删Formatters或者修改Formatters的顺序来调整默认的格式化方式。

Controller

当需要注册的路由方法比较多,而且之间有一定的逻辑关系时,可以定义一个类似的asp.net mvc的Controller对象,然后将路由指向这个对象。代码可以参考https://github.com/sdming/wk/blob/master/demo/basic/controller/basic.go

一个简单的注册Controller的例子如下:

  1. basic = NewBasicController()
  2. // url: /basic/xxx/xxx
  3. server.RouteTable.Path("/basic#/{action}/{id}").ToController(basic)

用正则表达式的话,例子如下

  1. srv.RouteTable.Regexp("*", "^/user/?(?P<action>[[:alnum:]]+)?/?(?P<arg>[[:alnum:]]+)?/?").ToController(NewUserController())

注册到controller的路由,一个特殊的路由参数是{action},它指定了调用Controller的哪一个方法。比如/basic/delete/32对应的action是"delete",调用controller的delete方法,一个例子如下:

  1. // url: /basic/delete/32
  2. func (c *BasicController) Delete(ctx *wk.HttpContext) (result wk.HttpResult, err error) {
  3. l := len(c.data)
  4. if i, ok := ctx.RouteData.Int("id"); ok {
  5. c.deleteByInt(i)
  6. }
  7. return wk.Data(l - len(c.data)), nil
  8. }

gwk现在版本的controller只支持func (ctx *wk.HttpContext) (result wk.HttpResult, err error)类型的方法,已经基本够用了。

再看一个例子:

  1. // url: /basic/add/?int=32&str=string&uint=1024&float=1.1&byte=64
  2. func (c *BasicController) Add(ctx *wk.HttpContext) (result wk.HttpResult, err error) {
  3. data := &model.Data{
  4. Int: ctx.FormIntOr("int", 0),
  5. Uint: uint64(ctx.FormIntOr("uint", 0)),
  6. Str: ctx.FormValue("str"),
  7. Float: float32(ctx.FormFloatOr("float", 0.0)),
  8. Byte: byte(ctx.FormIntOr("byte", 0)),
  9. }
  10. c.data = append(c.data, data)
  11. return wk.Data(data.String()), nil
  12. }

FormIntOr,FormFloatOr等函数是为了方便读取Request Form数据,可以参考RouteData的对应函数。

wk.Data函数返回 *DataResult对象,*DataResult实现了wk.HttpResult接口, HttpResult接口的详细介绍见后面的章节。

  1. func Data(data interface{}) *DataResult {
  2. return &DataResult{
  3. Data: data,
  4. }
  5. }

再看一个返回json数据的例子

  1. // url: /basic/int/32
  2. func (c *BasicController) Int(ctx *wk.HttpContext) (result wk.HttpResult, err error) {
  3. if id, ok := ctx.RouteData.Int("id"); ok {
  4. return wk.Json(c.getByInt(id)), nil
  5. }
  6. return wk.Data(""), nil
  7. }

再看一个直接读取post的数据,解析成json的例子:

  1. // url: post /basic/post
  2. func (c *BasicController) Post(ctx *wk.HttpContext) (result wk.HttpResult, err error) {
  3. var body []byte
  4. if body, err = ctx.ReadBody(); err != nil {
  5. return nil, err
  6. }
  7. data := &model.Data{}
  8. err = json.Unmarshal(body, data)
  9. if err != nil {
  10. return nil, err
  11. }
  12. c.data = append(c.data, data)
  13. return wk.Data(true), nil
  14. }

如果没有找到路由参数{action},gwk会将http method作为action,也就是把"get","post","delete"作为action,如果找不到对应的方法,会将{action}{method}组合起来作为action,这在一些场合还是比较实用的,如果还找不到对应的方法,则将"default"作为action,如果还是找不到对应的方法,则返回404.

各种流行的MVC类开发框架比较多,controller应该不用做过多的介绍,接下来介绍HttpResult接口。

Go语言Web框架gwk介绍 (一)的更多相关文章

  1. Go语言Web框架gwk介绍4

    Go语言Web框架gwk介绍 (四)   事件 gwk支持事件系统,但并没有硬编码有哪些事件,而是采用了比较松散的定义方式. 订阅事件有两种方式: 调用On函数或者OnFunc函数 func On(m ...

  2. Go语言Web框架gwk介绍 3

    Go语言Web框架gwk介绍 (三)   上一篇忘了ChanResult ChanResult 可以用来模拟BigPipe,定义如下 type ChanResult struct { Wait syn ...

  3. Go语言Web框架gwk介绍2

    Go语言Web框架gwk介绍 (二) HttpResult 凡是实现了HttpResult接口的对象,都可以作为gwk返回Web客户端的内容.HttpResult接口定义非常简单,只有一个方法: ty ...

  4. Go语言Web框架gwk介绍 1

    Go语言Web框架gwk介绍 (一)   今天看到Golang排名到前30名了,看来关注的人越来越多了,接下来几天详细介绍Golang一个web开发框架GWK. 现在博客园支持markdown格式发布 ...

  5. Go语言Web框架gwk介绍 (五)

    Session Go的net/http本身不带session的机制,需要开发人员自行实现,gwk实现了内存中的session存储机制,如果需要将session存在其他地方比如redis或者memcac ...

  6. Go语言Web框架gwk介绍 (四)

    事件 gwk支持事件系统,但并没有硬编码有哪些事件,而是采用了比较松散的定义方式. 订阅事件有两种方式: 调用On函数或者OnFunc函数 func On(moudle, name string, h ...

  7. Go语言Web框架gwk介绍 (二)

    HttpResult 凡是实现了HttpResult接口的对象,都可以作为gwk返回Web客户端的内容.HttpResult接口定义非常简单,只有一个方法: type HttpResult inter ...

  8. Go语言Web框架gwk介绍 (三)

    上一篇忘了ChanResult ChanResult 可以用来模拟BigPipe,定义如下 type ChanResult struct { Wait sync.WaitGroup Chan chan ...

  9. 最好的6个Go语言Web框架

    原文:Top 6 web frameworks for Go as of 2017 作者:Edward Marinescu 译者:roy 译者注:本文介绍截至目前(2017年)最好的6个Go语言Web ...

随机推荐

  1. 华夏部分互联网科技公司创始及IPO信息

    时间:2018-04-19 前面整理了一些美国科技公司的信息,这篇文章整理的是我华夏的一些科技公司的信息. 华为.百度.阿里.腾讯.美团.携程.京东.小米.奇虎360……之后,其它一些公司,要么体量 ...

  2. spring boot jpa 多数据源配置

    在实际项目中往往会使用2个数据源,这个时候就需要做额外的配置了.下面的配置在2.0.1.RELEASE 测试通过 1.配置文件 配置两个数据源 spring.datasource.url=jdbc:m ...

  3. python网络编程-动态导入和断言

    一:动态导入importlib 在程序运行的过程中,根据变量或者配置动态的决定导入哪个模块,可以使用模块importlib importlib使用示例 二:断言assert 如果接下来的程序依赖于前面 ...

  4. 1.SpringBoot之Helloword 快速搭建一个web项目

    背景: Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配 ...

  5. linux nat网络配置

    1. 2 . 3. BOOTPROTO = static ONBOOT=yes  #开启自动启用网络连接 IPADDR0=192.168.21.128  #设置IP地址 PREFIXO0=24  #设 ...

  6. dedecms自定义模型之独立模型在首页、列表页、内容调用内容

    dedecms关于自定义模型(独立模型)的首页.列表页.内容怎么调用?在后台自定义模型(独立模型)的建立及自定义字段的添加比较简单,需要注意两点: (1)如果某个字段需要在前台列表页显示,则在前台参数 ...

  7. Knockout应用开发指南 应用举例(简单、高级)

    Knockout应用开发指南 第八章:简单应用举例(1)http://www.cnblogs.com/TomXu/archive/2011/11/30/2257067.htmlKnockout应用开发 ...

  8. spark集群安装[转]

    [转]http://sofar.blog.51cto.com/353572/1352713 ====================================================== ...

  9. 一步一步学习IdentityServer3 (14) 启用Https

    申领一个CA证书,我用了一个腾讯的免费证书night-c.cn,这是腾讯云买域名送的一个证书 是单域名,非泛域名 1:安装证书  IIS选择证书 2:将证书加载到Identityserver3中,并启 ...

  10. 【58沈剑架构系列】为什么说要搞定微服务架构,先搞定RPC框架?

    第一章聊了[“为什么要进行服务化,服务化究竟解决什么问题”] 第二章聊了[“微服务的服务粒度选型”] 今天开始聊一些微服务的实践,第一块,RPC框架的原理及实践,为什么说要搞定微服务架构,先搞定RPC ...