Categorygithub.com/honmaple/forest
modulepackage
0.0.0-20240829025940-96b93e866722
Repository: https://github.com/honmaple/forest.git
Documentation: pkg.go.dev

# README

** Example #+begin_src go package main

 import (
     "net/http"

     "github.com/honmaple/forest"
     "github.com/honmaple/forest/middleware"
 )

 func main() {
     r := forest.New(forest.Debug())
     r.Use(middleware.Recover())
     r.Use(middleware.Logger())
     r.GET("/", func(c forest.Context) error {
         return c.HTML(http.StatusOK, "<h1>Hello Forest</h1>")
     })

     v1 := r.Group(forest.WithPrefix("/api"))
     {
         v1.GET("/posts/{title}", func(c forest.Context) error {
             return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
         })
         v1.POST("/posts", func(c forest.Context) error {
             type post struct {
                 Title   string `json:"title"   form:"title"`
                 Content string `json:"content" form:"content"`
             }
             p := post{}
             if err := c.Bind(&p); err != nil {
                 return c.JSON(http.BadRequest, forest.H{"message": err.Error()})
             }
             return c.JSON(http.StatusOK, p)
         })
     }

     v2 := forest.NewGroup(forest.WithHost("v2.localhost:8000"))
     {
         v2.GET("/posts/{title}", func(c forest.Context) error {
             return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
         })
     }

     r.Mount(v2)
     r.Start("127.0.0.1:8000")
 }

#+end_src

** Route

*** Single parameter in path #+begin_src go router := forest.New() // /posts/1 {"var": "1"} // /posts/test {"var": "test"} // /posts, /posts/, /posts/1/1 not match router.GET("/posts/:var", handler) // /posts/ {"var": ""} // /posts/1 {"var": "1"} // /posts/test {"var": "test"} // /posts, /posts/1/1 not match router.GET("/posts/:var?", handler) // /posts/ {"var": ""} // /posts/1 {"var": "1"} // /posts/1/ {"var": "1/"} // /posts/1/test/2 {"var": "1/test/2"} router.GET("/posts/*var", handler) // /posts/1 {"var": "1"} // /posts/test {"var": "test"} // /posts, /posts/, /posts/1/1 not match router.GET("/posts/{var}", handler) router.GET("/posts/{var:string}", handler) // /posts/ {"var": ""} // /posts/1 {"var": "1"} // /posts/test {"var": "test"} // /posts, /posts/1/1 not match router.GET("/posts/{var?}", handler) // /posts/1 {"var": "1"} // /posts/test not match router.GET("/posts/{var:int}", handler) // /posts/1 not match // /posts/test not match // /posts/.1 {"var": ".1"} // /posts/1.1 {"var": "1.1"} // /posts/1.10 {"var": "1.10"} router.GET("/posts/{var:float}", handler) // /posts/1 {"var": "1"} // /posts/test {"var": "test"} // /posts/test/1 {"var": "test/1"} router.GET("/posts/{var:path}", handler) #+end_src *** Multi parameters in path #+begin_src go // /posts/1 not match // /posts/prefixtest {"var": "test"} router.GET("/posts/prefix:var", handler) // /posts/prefixtest {"var:end": "test"} router.GET("/posts/prefix:var:end", handler) // /posts/test not match // /posts/test/1 not match // /posts/test/1/test {"var": "test/1"} router.GET("/posts/*var/test", handler) // /posts/test not match // /posts/test-123 {"var": "test", "var1": "123"} router.GET("/posts/{var}-{var1:int}", handler) // /posts/123-test {"var": "123", "var1": "test"} router.GET("/posts/{var:int}-{var1}", handler) // /posts/123-test {"var": "123", "var1": "test"} // /posts/123/test-test {"var": "123/test", "var1": "test"} router.GET("/posts/{var:path}-{var1}", handler) // /posts/1/1/test {"var": "1", "var1": "1", "var2": "test"} // /posts/test/1/1/test {"var": "test/1", "var1": "1", "var2": "test"} // /posts/test/1/1/s/test not match router.GET("/posts/{var:path}/{var1:int}/{var2}", handler) // /posts/1/1/test {"var": "1", "var1": "1", "var2": "test"} // /posts/test/1/1/test {"var": "test", "var1": "1", "var2": "1/test"} // /posts/test/1/1/s/test {"var": "test", "var1": "1", "var2": "1/s/test"} router.GET("/posts/{var:path}/{var1:int}/{var2:path}", handler) #+end_src

*** Named Route #+begin_src go r := forest.New() g1 := r.Group(forest.WithPrefix("/api"), forest.WithName("g1")) g2 := g1.Group(forest.WithPrefix("/v1"), forest.WithName("g2")) r1 := api.GET("/posts").Named("list_posts", "some description") r2 := api.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" #+end_src

*** Server Static files #+begin_src go r := forest.New() r.GET("/static/", func(c forest.Context) error { path := filepath.Join("static", c.Param("")) return c.FileFromFS(path, http.FS(staticFS)) }) r.GET("/robots.txt", func(c forest.Context) error { return c.FileFromFS("static/robots.txt", http.FS(staticFS)) }) r.GET("/favicon.ico", func(c forest.Context) error { return c.FileFromFS("static/favicon.ico", http.FS(staticFS)) }) #+end_src

*** Bind Params #+begin_src go 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"}) #+end_src

** Custom *** Custom Middleware #+begin_src go 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 }) #+end_src

*** Custom Logger #+begin_src go router := forest.New() router.Logger = Logger1

  router.GET("/posts", func(c forest.Context) error {
      // c.Logger() == Logger1
      ...
      })

  group := router.Group(forest.WithPrefix("/api/v1"))
  group.GET("/posts", func(c forest.Context) error {
      // c.Logger() == Logger1
      ...
      })

  group := router.Group(forest.WithPrefix("/api/v2"))
  group.Logger = Logger2
  group.GET("/posts", func(c forest.Context) error {
      // c.Logger() == Logger2
      ...
      })
#+end_src

*** Custom Error Handler #+begin_src go router := forest.New() // engine only router.NotFound(func(c forest.Context) error { return c.JSON(404, forest.H{"message": "not found"}) }) router.MethodNotAllowed(func(c forest.Context) error { return c.JSON(405, forest.H{"message": "method not allowed"}) })

  router.ErrorHandler = func(err error, c Context) {
      c.String(500, err.Error())
  }
  group := router.Group(forest.WithPrefix("/api/v1"))
  // group only
  group.ErrorHandler = func(err error, c Context) {
      c.String(501, err.Error())
  }
#+end_src

*** Custom Context #+begin_src go type MyContext struct { forest.Context }

  func (c *MyContext) Next() error {
      return c.NextWith(c)
  }

  func MyContextMiddleware(c forest.Context) error {
      // doing somthing
      return c.NextWith(&MyContext{c})
  }
#+end_src

*** Custom Host Matcher #+begin_src go func matcher(host, dst string) bool { return host == dst } r := forest.New(forest.HostMatch(matcher)) // or use internal matcher r := forest.New(forest.HostMatch(forest.HostMatcher)) #+end_src

*** Custom URL Param #+begin_src go 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)
#+end_src

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Functions

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Variables

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Structs

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Interfaces

No description provided by the author
No description provided by the author
No description provided by the author

# Type aliases

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author