自己在学习 Golang 的时候,心血来潮写了 GoGym,一个简洁灵活的 RESTful 微框架 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
leontung
V2EX    Go 编程语言

自己在学习 Golang 的时候,血来潮写了 GoGym,一个简洁灵活的 RESTful 微框架

  •  
  •   leontung
    ZhenhangTung 2017-03-13 18:33:04 +08:00 2079 次点击
    这是一个创建于 3137 天前的主题,其中的信息可能已经有所发展或是发生改变。

    受 PHP 框架 Laravel 的启发, GoGym 兼顾了代码的简洁易用性 框架解决的需求是

    1. 只需要定义一个 Controller 并且在注册了之后,可以定义任意数量的 Action ,只需要将其和路由还有方法匹配好
    2. 用户只要返回结果,自动生成 JSON 格式的 response

    示例代码:

    type IndexController struct { } func (IndexController *IndexController) Index(values url.Values, headers http.Header) (statusCode int, response interface{}) { return 200, map[string]string{"hello": "world"} } func main() { var apiService = GoGym.Prepare() apiService.Get("/", "IndexController@Index") apiService.RegisterController(&IndexController{}) apiService.Serve(3000) } 

    可以看到,我们只需要上面短短的 11 行代码,就能起一个 hello world 的 RESTful 服务

    欢迎大家发 issue 或者邮件来讨论不足的地方,让我可以改进,也可以提交 feature requirement ,如果觉得不错也欢迎 star

    项目地址: https://github.com/ZhenhangTung/GoGym

    八卦一下,起这个项目名字就是因为自己喜欢去健身房,对自己也是一种督促,也希望大家多多运动保重身体,敲代码是产出的话,休息运动就是给自己充值了。

    共勉!

    第 1 条附言    2017-03-19 08:49:32 +08:00

    Notice: 我已经将GoGym中的功能一个个拆分,作为服务的存在,并定义了服务的接口,还开放了RegisterService()来支持用户注册自己定义的服务到Service Container中,这对于初始的版本是个重大的改变。但由于Go的语言特性,对于用户注册的服务,调用方法会使用到reflcet,我也提供了一个helper方法CallMethod()来实现调用,如果用户想自己写也没有问题。

    示例代码如下:

    package main import ( "fmt" "github.com/ZhenhangTung/GoGym" "net/http" "reflect" ) type HelloController struct { } func (h *HelloController) SayHello(api *GoGym.Gym) { api.Response.JsonResponse(map[string]string{"hello": "world"}, 200, http.Header{}) } type FooService struct { boss *GoGym.Gym } func (f *FooService) Prepare(g *GoGym.Gym) { f.WhoIsYourBoss(g) } func (f *FooService) WhoIsYourBoss(g *GoGym.Gym) { f.boss = g } func (f *FooService) CallYourBoss() *GoGym.Gym { return f.boss } func (f *FooService) Test() { fmt.Println("oh yes") } func (f *FooService) CallMethod(method string, param []interface{}) []reflect.Value { r := GoGym.CallServiceMethodWithReflect(f, method, param) return r } func main() { var gym = new(GoGym.Gym) gym.Prepare() gym.Router.RegisterController(&HelloController{}) gym.Router.Get("/", "HelloController@SayHello") gym.RegisterService("Foo", new(FooService)) gym.GetService("Foo").CallMethod("Test", nil) gym.OpenAt(3000) } 

    具体详情还请移步去看项目的文档。 感谢!

    15 条回复    2017-04-12 18:41:14 +08:00
    taowen
        1
    taowen  
       2017-03-14 08:52:13 +08:00
    支持一下,造轮子不容易
    leontung
        2
    leontung  
    OP
       2017-03-14 13:59:50 +08:00
    @taowen 十分感谢!大家一起加油
    jarlyyn
        3
    jarlyyn  
       2017-03-15 15:56:27 +08:00
    看不懂。

    这是要注册个 controller 类,实现 controller interface ?

    直接用 httprouter 就可以了啊

    var jsOnAction= func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    body, _ := json.Marshal(map[string]string{"hello": "world"})
    w.Write(body)
    }
    router := httprouter.New()
    router.RedirectTrailingSlash = false
    router.RedirectFixedPath = false
    router.GET("/test", jsonAction)
    http.Handle("/controller/", http.StripPrefix("/controller", router))
    http.ListenAndServe(":3000", http.DefaultServeMux)
    jarlyyn
        4
    jarlyyn  
       2017-03-15 16:10:36 +08:00
    要说看得出来的问题的话,感觉以字符串形式传 action 太坑

    及不能类型检测,不能错误检查。
    leontung
        5
    leontung  
    OP
       2017-03-15 22:57:15 +08:00
    @jarlyyn 十分感谢!我之前都没发现有 router 这么厉害的项目。
    我是在学习过程中,发现 Golang 很多 web 框架,比如 echo 等,都会先预定好一些方法( Action ),比如在 Controller(有些框架定义为 router)中有 GET , POST 等方法来接收 GET 和 POST 等请求,这样很不错,也符合 Golang 的语言特性,但我觉得不太能满足业务需求,如果我有一个 Controller ,就是负责首页逻辑的,需要接收 2 个 GET 方法,按照目前我的理解来看,我就要创建两个 controller 了。
    我解决的事情是,只需要建立一个 Controller ,下面可以定义无数个 Action 。
    用 apiService.Get("/", "IndexController@Index")注册了后,在底层就会生成一个包含 http request , route , Controller , Action 的 map ,最后我用的是 reflect 去调用所有的 Action 。
    你提到的类型检测,不能错误检查这两点我会考虑并且看看怎么加入。
    jarlyyn
        6
    jarlyyn  
       2017-03-15 23:51:01 +08:00
    @leontung

    一个 router 就是一个控制器。

    想一想,写 web 为什么一定要有控制器类呢?

    方法 /函数本身就可以作为变量,所以其实不一定需要控制器类这个概念。

    还有就是。

    要代码补全的话,直接吧 action 传进去就可以了。

    比如

    indexController:=&IndexController{}

    apiService.Get("/", indexCroller.index)
    jarlyyn
        7
    jarlyyn  
       2017-03-15 23:53:34 +08:00
    @leontung

    以我的 blog 为例

    https://gist.github.com/jarlyyn/57ac8e2dc950e9c7715de27149481319

    用了两个 router

    其实不就等于是两个控制器?
    leontung
        8
    leontung  
    OP
       2017-03-16 11:00:00 +08:00
    @jarlyyn 的确没有任何规定要规定大家都用 controller class ,然后在里面定义一系列 method 作为 action 来开发项目呢,就像写代码也不一定要用 OOP 思想来写。这些思想需要我们在某些前提下讨论。
    如果我写一个小项目(我觉得自己的个人博客也算其中之一吧),或者小脚本,不管是 golang , php 还是 js ,以我目前的浅薄的开发经验,都不需要建立 class ( Golang 里是 struct ),搞个 class 来有时甚至只是种累赘。
    但如果我们的项目发展到一定程度了呢,有同事朋友或者其他开发者参与进来了呢,考虑的不只是实现了,我们还要考虑 organization ,往大一点说就是架构,考虑他人的开发感受,我认为这就是会有各种设计模式的原因吧,大家在同一个 convention 下开发(注意,我没有用 law 这个词)。
    我的小项目目标是“ RESTful API ”。
    扯开点说,代码除了代码本身的语法和规定,怎么写都是大家自己的决定, free style 都可以,只是在每个情景之下都会有 pros&cons ,是适合不适合的问题,并没有对错。
    ---
    我:PHP 是世界上最好的语言,不是吗?
    A:Java 才是
    B:Bullshit!
    ....
    2333~
    jarlyyn
        9
    jarlyyn  
       2017-03-16 11:59:54 +08:00
    @leontung

    middleware 本身就是一种组织形式啊。

    不过是组件化 Vs 继承罢了。

    从某种角度来说,使用控制器类并不一定适合,或者从我的经验来看不适合大规模的项目。大项目最简单的降低难度的方式就是拆散。调试 /继承中间件在这个情况下比调整控制器类容易的多。

    当然,这点并不重要。我只是一些建议和意见罢了。

    我只是在写了几年 php 后歪到 nodejs 去了,受 express/koa 影响多了,觉得 middleware 型的框架水到渠成罢了。
    leontung
        10
    leontung  
    OP
       2017-03-16 19:09:22 +08:00
    @jarlyyn 嗯嗯,理解,你的建议很棒,我自己已经开好了 issue 着手改造了。
    我是写了 PHP 最近因为公司业务需求歪到了 Golang 哈哈哈。
    leontung
        11
    leontung  
    OP
       2017-03-16 19:13:48 +08:00
    @jarlyyn middleware 我还不是特别熟悉,在 laravel5 引入了 middleware 时候我一直在用 Phalcon 写业务,没有机会去学,我去调研学习一下这个概念
    102400
        12
    102400  
       2017-04-10 02:29:37 +08:00
    Laravel 本身比很多 web 框架都要麻烦复杂得多,用它的方式写 go 还不如不要
    leontung
        13
    leontung  
    OP
       2017-04-11 22:50:09 +08:00
    @102400 嗯嗯,在研究 Laravel 源码时候,拿路由举例,它使用了一些 Symfony 路由中的 Compile ,然后拿到结果自己再处理,我个人认为太复杂,所以我自己在实现路由时候并没有用那一套。
    102400
        14
    102400  
       2017-04-12 00:56:43 +08:00
    @leontung Ruby 的 Sinatra 几乎是这类框架的典范,可以参考下
    leontung
        15
    leontung  
    OP
       2017-04-12 18:41:14 +08:00
    @102400 谢谢指教!不过我现在还不会 Ruby ,阅读细节可能会比较费劲,可以看下大局的思路
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3736 人在线   最高记录 6679     < href="/select/language" class="f11">   Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 00:45 PVG 08:45 LAX 17:45 JFK 20:45
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86