gookit/rux 简单且快速的 Go Web 框架

发布于 2021-07-02 19:11:02 字数 11079 浏览 1345 评论 0

rux 简单且快速的 Go web 框架,支持中间件,兼容 http.Handler 接口。

NOTICE: v1.3.x 不完全兼容 v1.2.x 版本

  • 支持路由参数,支持路由组,支持给路由命名
  • 支持方便的静态文件/目录处理
  • 支持缓存最近访问的动态路由以获得更高性能
  • 支持中间件: 路由中间件,组中间件,全局中间件
  • 兼容支持 http.Handler 接口,可以直接使用其他的常用中间件
  • 支持添加 NotFoundNotAllowed 处理
  • 支持添加 ErrorPanic 处理错误或异常

安装

go get github.com/gookit/rux

快速开始

package main

import (
  "github.com/gookit/rux"
)

func main() {
  r := rux.New()
  
  // ===== 静态资源
  // 单个文件
  r.StaticFile("/site.js", "testdata/site.js")
  // 静态资源目录
  r.StaticDir("/static", "testdata")
  // 静态资源目录,但是有后缀限制
  r.StaticFiles("/assets", "testdata", "css|js")

  // ===== 添加路由
  
  r.GET("/", func(c *rux.Context) {
    c.Text(200, "hello")
  })
  r.GET("/hello/{name}", func(c *rux.Context) {
    c.Text(200, "hello " + c.Param("name"))
  })
  r.POST("/post", func(c *rux.Context) {
    c.Text(200, "hello")
  })
  r.Group("/articles", func() {
    r.GET("", func(c *rux.Context) {
      c.Text(200, "view list")
    })
    r.POST("", func(c *rux.Context) {
      c.Text(200, "create ok")
    })
    r.GET(`/{id:\d+}`, func(c *rux.Context) {
      c.Text(200, "view detail, id: " + c.Param("id"))
    })
  })

  // 快速添加多个METHOD支持
  r.Add("/post[/{id}]", func(c *rux.Context) {
    if c.Param("id") == "" {
      // do create post
      c.Text(200, "created")
      return
    }
    
    id := c.Params.Int("id")
    // do update post
    c.Text(200, "updated " + fmt.Sprint(id))
  }, rux.POST, rux.PUT)

  // 启动服务并监听
  r.Listen(":8080")
  // 也可以
  // http.ListenAndServe(":8080", r)
}

使用中间件

支持使用中间件:

  • 全局中间件
  • 路由组中间件
  • 路由中间件

调用优先级: 全局中间件 -> 路由组中间件 -> 路由中间件

使用示例:

package main

import (
  "fmt"
  
  "github.com/gookit/rux"
)

func main() {
  r := rux.New()
  
  // 添加一个全局中间件
  r.Use(func(c *rux.Context) {
      // do something ...
  })
  
  // 通过参数添加中间件
  route := r.GET("/middle", func(c *rux.Context) { // main handler
    c.WriteString("-O-")
  }, func(c *rux.Context) { // middle 1
        c.WriteString("a")
        c.Next() // Notice: call Next()
        c.WriteString("A")
    // if call Abort(), will abort at the end of this middleware run
    // c.Abort() 
    })

  // 通过 Use() 添加中间件
  route.Use(func(c *rux.Context) { // middle 2
    c.WriteString("b")
    c.Next()
    c.WriteString("B")
  })

  // 启动server访问: /middle
  // 将会看到输出: ab-O-BA
}
  • 调用流程图:
        +-----------------------------+
        | middle 1                    |
        |  +----------------------+   |
        |  | middle 2             |   |
 start  |  |  +----------------+  |   | end
------->|  |  |  main handler  |  |   |--->----
        |  |  |________________|  |   |    
        |  |______________________|   |  
        |_____________________________|

更多的使用请查看 middleware_test.go 中间件测试

使用http.Handler

rux 支持通用的 http.Handler 接口中间件

你可以使用 rux.WrapHTTPHandler() 转换 http.Handlerrux.HandlerFunc

package main

import (
  "net/http"
  
  "github.com/gookit/rux"
  // 这里我们使用 gorilla/handlers,它提供了一些通用的中间件
  "github.com/gorilla/handlers"
)

func main() {
  r := rux.New()
  
  // create a simple generic http.Handler
  h0 := http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
    w.Header().Set("new-key", "val")
  })
  
  r.Use(rux.WrapHTTPHandler(h0), rux.WrapHTTPHandler(handlers.ProxyHeaders()))
  
  r.GET("/", func(c *rux.Context) {
    c.Text(200, "hello")
  })
  // add routes ...
  
  // Wrap our server with our gzip handler to gzip compress all responses.
  http.ListenAndServe(":8000", handlers.CompressHandler(r))
}

更多功能

路由命名

rux 中你可以添加命名路由,根据名称可以从路由器里拿到对应的路由实例 rux.Route

有几种方式添加命名路由:

  r := rux.New()
  
  // Method 1
  myRoute := rux.NewNamedRoute("name1", "/path4/some/{id}", emptyHandler, "GET")
  r.AddRoute(myRoute)

  // Method 2
  rux.AddNamed("name2", "/", func(c *rux.Context) {
    c.Text(200, "hello")
  })

  // Method 3
  r.GET("/hi", func(c *rux.Context) {
    c.Text(200, "hello")
  }).NamedTo("name3", r)
  
  // get route by name
  myRoute = r.GetRoute("name1")

重定向跳转

r.GET("/", func(c *rux.Context) {
    c.AbortThen().Redirect("/login", 302)
})

// Or
r.GET("/", func(c *rux.Context) {
    c.Redirect("/login", 302)
    c.Abort()
})

r.GET("/", func(c *rux.Context) {
    c.Back()
    c.Abort()
})

操作Cookies

您可以通过以下方式快速操作Cookies FastSetCookie() DelCookie()

注意:您必须先设置或删除Cookies,然后再调用写入BODY内容的相关方法

r.GET("/setcookie", func(c *rux.Context) {
    c.FastSetCookie("rux_cookie2", "test-value2", 3600)
    c.SetCookie("rux_cookie", "test-value1", 3600, "/", c.Req.URL.Host, false, true)
  c.WriteString("hello, in " + c.URL().Path)
})

r.GET("/delcookie", func(c *rux.Context) {
  val := ctx.Cookie("rux_cookie") // "test-value1"
  c.DelCookie("rux_cookie", "rux_cookie2")
})

多个域名

code is ref from julienschmidt/httprouter

package main

import (
  "log"
  "net/http"

  "github.com/gookit/rux"
)

type HostSwitch map[string]http.Handler

// Implement the ServeHTTP method on our new type
func (hs HostSwitch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  // Check if a http.Handler is registered for the given host.
  // If yes, use it to handle the request.
  if router := hs[r.Host]; router != nil {
    router.ServeHTTP(w, r)
  } else {
    // Handle host names for which no handler is registered
    http.Error(w, "Forbidden", 403) // Or Redirect?
  }
}

func main() {
  // Initialize a router as usual
  router := rux.New()
  router.GET("/", Index)
  router.GET("/hello/{name}", func(c *rux.Context) {})

  // Make a new HostSwitch and insert the router (our http handler)
  // for example.com and port 12345
  hs := make(HostSwitch)
  hs["example.com:12345"] = router

  // Use the HostSwitch to listen and serve on port 12345
  log.Fatal(http.ListenAndServe(":12345", hs))
}

RESETful 风格

package main

import (
  "log"
  "net/http"

  "github.com/gookit/rux"
)

type Product struct {
}

// middlewares [optional]
func (Product) Uses() map[string][]rux.HandlerFunc {
  return map[string][]rux.HandlerFunc{
    // function name: handlers
    "Delete": []rux.HandlerFunc{
      handlers.HTTPBasicAuth(map[string]string{"test": "123"}),
      handlers.GenRequestID(),
    },
  }
}

// all products [optional]
func (p *Product) Index(c *rux.Context) {
  // do something ...
}

// create product [optional]
func (p *Product) Create(c *rux.Context) {
  // do something ...
}

// save new product [optional]
func (p *Product) Store(c *rux.Context) {
  // do something ...
}

// show product with {id} [optional]
func (p *Product) Show(c *rux.Context) {
  // do something ...
}

// edit product [optional]
func (p *Product) Edit(c *rux.Context) {
  // do something ...
}

// save edited product [optional]
func (p *Product) Update(c *rux.Context) {
  // do something ...
}

// delete product [optional]
func (p *Product) Delete(c *rux.Context) {
  // do something ...
}

func main() {
  router := rux.New()

  // methods  Path  Action  Route Name
    // GET  /product  index  product_index
    // GET  /product/create  create  product_create
    // POST  /product  store  product_store
    // GET  /product/{id}  show  product_show
    // GET  /product/{id}/edit  edit  product_edit
    // PUT/PATCH  /product/{id}  update  product_update
    // DELETE  /product/{id}  delete  product_delete
    // resetful style
  router.Resource("/", new(Product))

  log.Fatal(http.ListenAndServe(":12345", router))
}

Controller 风格

package main

import (
  "log"
  "net/http"

  "github.com/gookit/rux"
)

// News controller
type News struct {
}

func (n *News) AddRoutes(g *rux.Router) {
  g.GET("/", n.Index)
  g.POST("/", n.Create)
  g.PUT("/", n.Edit)
}

func (n *News) Index(c *rux.Context) {
  // Do something
}

func (n *News) Create(c *rux.Context) {
  // Do something
}

func (n *News) Edit(c *rux.Context) {
  // Do something
}


func main() {
  router := rux.New()

  // controller style
  router.Controller("/news", new(News))

  log.Fatal(http.ListenAndServe(":12345", router))
}

获取路由与生成请求URL

package main

import (
  "log"
  "net/http"
  "net/url"

  "github.com/gookit/rux"
)

func main() {
  // Initialize a router as usual
  router := rux.New()
  router.GET(`/news/{category_id}/{new_id:\d+}/detail`, func(c *rux.Context) {
    var u = make(url.Values)
      u.Add("username", "admin")
      u.Add("password", "12345")
    
    b := rux.NewBuildRequestURL()
      // b.Scheme("https")
      // b.Host("www.mytest.com")
      b.Queries(u)
      b.Params(rux.M{"{category_id}": "100", "{new_id}": "20"})
    // b.Path("/dev")
      // println(b.Build().String())
      
      println(c.Router().BuildRequestURL("new_detail", b).String())
    // result:  /news/100/20/detail?username=admin&password=12345
    // get current route name
    if c.MustGet(rux.CTXCurrentRouteName) == "new_detail" {
          // post data etc....
      }
  }).NamedTo("new_detail", router)

  // Use the HostSwitch to listen and serve on port 12345
  log.Fatal(http.ListenAndServe(":12345", router))
}

相关项目

github 地址:https://github.com/gookit/rux

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文