基于 Go net/http 开发的轻量路由框架-forest - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
honmaple
V2EX    分享创造

基于 Go net/http 开发的轻量路由框架-forest

  •  
  •   honmaple 2022-11-15 00:00:11 +08:00 2502 次点击
    这是一个创建于 1066 天前的主题,其中的信息可能已经有所发展或是发生改变。

    很早就写完了,貌似还没分享过。

    之前阅读过 Gin ,Echo 的源码,觉得 Go 里面基于net/http写一个自己的路由框架还挺简单的,然后就动手了,没想到看起来容易,部分细节的处理还是挺麻烦的

    与 Gin 和 Echo 的区别

    Gin 和 Echo 我比较喜欢 Echo 的设计,所以很多地方都借鉴了 Echo ,比如 Context 是接口类型,每一个路由处理函数都会返回 error ,而 Gin 里借鉴里中间件的实现,使用一个列表变量+索引的方式实现,未使用闭包

    路由

    使用了自己写的 radix tree ,动态路由性能上可能比 Gin 和 Echo 弱了一丢丢,静态路由性能要高一丢丢,测试可见 https://github.com/honmaple/forest/tree/master/examples/benchmark

    • 兼容 Gin 和 Echo 的写法: /api/users/:id或者/api/files/*filename
    • 一个 URI 使用多个任意参数: /api/users/{id:int}/friends/{avatar:path},甚至path在前也可以,不过路由查找性能会下降
    • 正则路由: /api/users/{id:[0-9]+}
    • 可扩展的参数路由:
    import ( "github.com/google/uuid" ) type UUIDMatcher struct { } func (s *UUIDMatcher) Name() string { return "uuid" } func (s *UUIDMatcher) Match(path string, index int, next bool) (int, bool) { if index > 0 { return 0, false } if len(path) < 18 || (!next && len(path) > 18) { return 0, false } _, err := uuid.Parse(path[:18]) if err != nil { return 0, false } return 18, true } func NewUUIDMatcher(rule string) forest.Matcher { return &UUIDMatcher{} } forest.RegisterRule("uuid", NewUUIDMatcher) router := forest.New() router.GET("/api/v1/user/{pk:uuid}", handler) 

    参数绑定

    type Params struct { Text string `query:"text" json:"text" form:"text" param:"text"` } p := Params{} // bind query, method: not POST, PUT, PATCH // bind form or json or xml, method: POST, PUT, PATCH c.Bind(&p) // bind params, GET /test/:text c.BindParams(&p) // bind other params c.BindWith(&p, bind.Query) c.BindWith(&p, bind.Form) c.BindWith(&p, bind.MultipartForm) c.BindWith(&p, bind.JSON) c.BindWith(&p, bind.XML) c.BindWith(&p, bind.Params) c.BindWith(&p, bind.Header) // custom bind tag c.BindWith(&p, bind.FormBinder{"json"}) c.BindWith(&p, bind.QueryBinder{"json"}) 

    中间件

    中间件借鉴了 Gin ,使用一个列表变量+索引,而不是 Echo 多个中间件嵌套闭包的方式

    func MyMiddleware(c forest.Context) error { // do something // c.Next() is required, or else your handler will not execute return c.Next() } router := forest.New() // with root router.Use(MyMiddleware) // with group group := router.Group(forest.WithPrefix("/api/v1"), forest.WithMiddlewares(MyMiddleware)) // with special handler group.GET("/", MyMiddleware, func(c forest.Context) error { return nil }) 

    命名路由

    我自己的需求就是开发后台管理系统配置路由权限时为什么要手动输入每一条路由以及它们的 unique name 和描述,所以内置了几个变量,可以在定义时就对路由进行命名

    r := forest.New() g1 := r.Group(forest.WithPrefix("/api"), forest.WithName("g1")) g2 := g1.Group(forest.WithPrefix("/v1"), forest.WithName("g2")) r1 := g2.GET("/posts").Named("list_posts", "some description") r2 := g2.DELETE("/posts/:pk").Named("delete_post", "delete post with pk param") // result r.Route("g1.g2.list_posts") == r1 r.URL("g1.g2.list_posts") == r1.URL() == "/v1/api/posts" r.Route("g1.g2.delete_post") == r2 r.URL("g1.g2.delete_post", "12") == r2.URL("12") == "/v1/api/posts/12" 

    想要获取全部路由,则可以遍历c.Forest().Routes()

    routes := c.Forest().Routes() ins := make([]forest.H, 0, len(routes)) for _, r := range routes { ins = append(ins, forest.H{ "id": fmt.Sprintf("%s %s", r.Method(), r.Path()), "name": r.Name, "path": r.Path(), "desc": r.Desc(), "method": r.Method(), }) } 

    除此之外,还有静态文件,自定义错误,自定义 Context ,自定义子域名匹配等功能,有很多功能都是我自己在开发时的需求,觉得有用就增加到 forest 里面,有兴趣的可以看一下

    地址: https://github.com/honmaple/forest

    7 条回复    2022-11-18 16:17:48 +08:00
    sunorg
        1
    sunorg  
       2022-11-15 00:03:06 +08:00 via Android
    来个特别大的特点?
    honmaple
        2
    honmaple  
    OP
       2022-11-15 00:23:57 +08:00
    @sunorg 正则路由和可扩展参数路由不知道算不算,还有就是路径参数可以放在 url 中间,我看很多路由框架都是只能放到末尾;以及路由分组可挂载,不受主路由影响,方便扩展;其它的一些扩展库 session ,swagger 也有,不过这应该属于常规功能 https://github.com/honmaple/forest-contrib

    web 路由框架基本的功能都差不多,重量级的特点还未曾想到,如果有什么建议我可以看看能否实现
    murongxdb
        3
    murongxdb  
       2022-11-15 08:47:56 +08:00
    先来一个 star
    spatxos
        4
    spatxos  
       2022-11-15 08:49:42 +08:00
    已 star
    40EaE5uJO3Xt1VVa
        5
    40EaE5uJO3Xt1VVa  
       2022-11-15 09:24:26 +08:00
    第三个 star
    sunorg
        6
    sunorg  
       2022-11-15 13:47:04 +08:00 via Android
    @honmaple 不算,但必须来个 star 支持下。
    EZVIK
        7
    EZVIK  
       2022-11-18 16:17:48 +08:00
    最近也在学习写路由解析,star 学习一下
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4182 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 00:13 PVG 08:13 LAX 17:13 JFK 20:13
    Do have faith in what you're doing.
    ubao msn 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