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 · 1587 次点击
    这是一个创建于 2594 天前的主题,其中的信息可能已经有所发展或是发生改变。

    受 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   ·   我们的愿景   ·   实用小工具   ·   2570 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 04:30 · PVG 12:30 · LAX 21:30 · JFK 00:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.