Skip to content

Commit 1f4c72f

Browse files
committed
add benchmark, add some test, update readme
1 parent d417f20 commit 1f4c72f

File tree

15 files changed

+1341
-124
lines changed

15 files changed

+1341
-124
lines changed

README.org

Lines changed: 171 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,180 @@
1+
** Example
2+
#+begin_src go
3+
package main
4+
5+
import (
6+
"net/http"
7+
8+
"github.com/honmaple/forest"
9+
"github.com/honmaple/forest/middleware"
10+
)
11+
12+
func main() {
13+
r := forest.New()
14+
r.Use(middleware.Recover())
15+
r.Use(middleware.Logger())
16+
r.GET("/", func(c forest.Context) error {
17+
return c.HTML(http.StatusOK, "<h1>Hello Forest</h1>")
18+
})
19+
20+
v1 := r.Group("/v1")
21+
{
22+
v1.GET("/posts/{title}", func(c forest.Context) error {
23+
return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
24+
})
25+
v1.POST("/posts", func(c forest.Context) error {
26+
return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
27+
})
28+
}
29+
30+
v2 := forest.NewHost("v2.localhost:8000")
31+
{
32+
v2.GET("/posts/{title}", func(c forest.Context) error {
33+
return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
34+
})
35+
}
36+
37+
r.Mount("", v2)
38+
r.Start("127.0.0.1:8000")
39+
}
40+
#+end_src
41+
142
** Route
2-
#+begin_example
3-
/path/:var
4-
/path/pre:var
5-
/path/:var1/:var2
6-
/path/*var
7-
/path/pre*var
8-
/path/{var}
9-
/path/{var:str}
10-
/path/{var:int}
11-
/path/{var:float}
12-
/path/{var:[0-9]+}
13-
/path/{var:path}
14-
/path/{var1}-{var2}
15-
/path/{var1}-{var2}/{var3}
16-
#+end_example
1743

18-
** Example
19-
#+begin_src go
20-
package main
21-
22-
import (
23-
"net/http"
24-
25-
"github.com/honmaple/forest"
26-
"github.com/honmaple/forest/middleware"
27-
)
28-
29-
func main() {
30-
r := forest.New()
31-
r.Use(middleware.Recover())
32-
r.Use(middleware.Logger())
33-
r.GET("/", func(c forest.Context) error {
34-
return c.HTML(http.StatusOK, "<h1>Hello Forest</h1>")
35-
})
44+
*** Single parameter in path
45+
#+begin_src go
46+
router := forest.New()
47+
// /posts/1 {"var": "1"}
48+
// /posts/test {"var": "test"}
49+
// /posts, /posts/, /posts/1/1 not match
50+
router.GET("/posts/:var", handler)
51+
// /posts/1 {"var": "1"}
52+
// /posts/1/ {"var": "1/"}
53+
// /posts/1/test/2 {"var": "1/test/2"}
54+
router.GET("/posts/*var", handler)
55+
// /posts/1 {"var": "1"}
56+
// /posts/test {"var": "test"}
57+
// /posts, /posts/, /posts/1/1 not match
58+
router.GET("/posts/{var}", handler)
59+
router.GET("/posts/{var:str}", handler)
60+
// /posts/1 {"var": "1"}
61+
// /posts/test not match
62+
router.GET("/posts/{var:int}", handler)
63+
// /posts/1 not match
64+
// /posts/test not match
65+
// /posts/.1 {"var": ".1"}
66+
// /posts/1.1 {"var": "1.1"}
67+
// /posts/1.10 {"var": "1.10"}
68+
router.GET("/posts/{var:float}", handler)
69+
// /posts/1 {"var": "1"}
70+
// /posts/test {"var": "test"}
71+
// /posts/test/1 {"var": "test/1"}
72+
router.GET("/posts/{var:path}", handler)
73+
#+end_src
74+
*** Multi parameters in path
75+
#+begin_src go
76+
// /posts/1 not match
77+
// /posts/prefixtest {"var": "test"}
78+
router.GET("/posts/prefix:var", handler)
79+
// /posts/prefixtest {"var:end": "test"}
80+
router.GET("/posts/prefix:var:end", handler)
81+
// /posts/test {"var": "test"}
82+
// /posts/test/1 {"var": "test/1"}
83+
router.GET("/posts/*var/test", handler)
84+
// /posts/test not match
85+
// /posts/test-123 {"var": "test", "var1": "123"}
86+
router.GET("/posts/{var}-{var1:int}", handler)
87+
// /posts/123-test {"var": "123", "var1": "test"}
88+
router.GET("/posts/{var:int}-{var1}", handler)
89+
// /posts/123-test {"var": "123", "var1": "test"}
90+
// /posts/123/test-test {"var": "123/test", "var1": "test"}
91+
router.GET("/posts/{var:path}-{var1}", handler)
92+
// /posts/1/1/test {"var": "1", "var1": "1", "var2": "test"}
93+
// /posts/test/1/1/test {"var": "test/1", "var1": "1", "var2": "test"}
94+
// /posts/test/1/1/s/test not match
95+
router.GET("/posts/{var:path}/{var1:int}/{var2}", handler)
96+
// /posts/1/1/test {"var": "1", "var1": "1", "var2": "test"}
97+
// /posts/test/1/1/test {"var": "test", "var1": "1", "var2": "1/test"}
98+
// /posts/test/1/1/s/test {"var": "test", "var1": "1", "var2": "1/s/test"}
99+
router.GET("/posts/{var:path}/{var1:int}/{var2:path}", handler)
100+
#+end_src
101+
102+
** Custom
103+
*** Custom Middleware
104+
#+begin_src go
105+
func mymiddleware(c forest.Context) error {
106+
// do something
107+
// c.Next() is required, or else your handler will not execute
108+
return c.Next()
109+
}
110+
router := forest.New()
111+
// with root
112+
router.Use(mymiddleware)
113+
// with group
114+
group := router.Group("/api/v1", mymiddleware)
115+
// with special handler
116+
group.GET("/", func(c forest.Context) error {
117+
return nil
118+
}, mymiddleware)
119+
#+end_src
120+
121+
*** Custom Logger
122+
#+begin_src go
123+
router := forest.New()
124+
router.Logger = Logger1
36125

37-
v1 := r.Group("/v1")
38-
{
39-
v1.GET("/posts/{title}", func(c forest.Context) error {
40-
return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
126+
router.GET("/posts", func(c forest.Context) error {
127+
// c.Logger() == Logger1
128+
...
41129
})
42-
v1.POST("/posts", func(c forest.Context) error {
43-
return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
130+
131+
group := router.Group("/api/v1")
132+
group.GET("/posts", func(c forest.Context) error {
133+
// c.Logger() == Logger1
134+
...
44135
})
45-
}
46136

47-
v2 := forest.NewHost("v2.localhost:8000")
48-
{
49-
v2.GET("/posts/{title}", func(c forest.Context) error {
50-
return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
137+
group := router.Group("/api/v2")
138+
group.Logger = Logger2
139+
group.GET("/posts", func(c forest.Context) error {
140+
// c.Logger() == Logger2
141+
...
51142
})
143+
#+end_src
144+
145+
*** Custom Error Handler
146+
#+begin_src go
147+
router := forest.New()
148+
// engine only
149+
router.NotFound(func(c forest.Context) error {
150+
return c.JSON(404, forest.H{"message": "not found"})
151+
})
152+
router.MethodNotAllowed(func(c forest.Context) error {
153+
return c.JSON(405, forest.H{"message": "method not allowed"})
154+
})
155+
156+
router.ErrorHandler = func(err error, c Context) {
157+
c.String(500, err.Error())
158+
}
159+
group := router.Group("/api/v1")
160+
// group only
161+
group.ErrorHandler = func(err error, c Context) {
162+
c.String(501, err.Error())
163+
}
164+
#+end_src
165+
166+
*** Custom Context
167+
#+begin_src go
168+
type MyContext struct {
169+
forest.Context
52170
}
53171

54-
r.Mount("", v2)
55-
r.Start("127.0.0.1:8000")
56-
}
57-
#+end_src
172+
func (c *MyContext) Next() error {
173+
return c.NextWith(c)
174+
}
175+
176+
func MyContextMiddleware(c forest.Context) error {
177+
// doing somthing
178+
return c.NextWith(&MyContext{c})
179+
}
180+
#+end_src

binder/binder.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var (
2121
Query = QueryBinder{"query"}
2222
Header = HeaderBinder{"header"}
2323
MultipartForm = MultipartFormBinder{"form"}
24+
Params = ParamsBinder{"param"}
2425
)
2526

2627
type JSONBinder struct{}
@@ -73,6 +74,18 @@ func (b HeaderBinder) Bind(req *http.Request, dst interface{}) error {
7374
return bindData(dst, req.Header, b.TagName)
7475
}
7576

77+
type ParamsBinder struct {
78+
TagName string
79+
}
80+
81+
func (b ParamsBinder) Bind(params map[string]string, dst interface{}) error {
82+
m := make(map[string][]string)
83+
for k, v := range params {
84+
m[k] = []string{v}
85+
}
86+
return bindData(dst, m, b.TagName)
87+
}
88+
7689
func Bind(req *http.Request, dst interface{}) (err error) {
7790
method := req.Method
7891
if method != http.MethodPost && method != http.MethodPut && method != http.MethodPatch {

context.go

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,35 @@ type H map[string]interface{}
1515

1616
type Context interface {
1717
Logger() Logger
18-
Next() error
19-
NextWith(Context) error
2018
Request() *http.Request
2119
Response() *Response
2220

21+
Next() error
22+
NextWith(Context) error
23+
2324
Get(string) interface{}
2425
Set(string, interface{})
2526

2627
Param(string) string
2728
Params() map[string]string
2829

30+
Bind(interface{}) error
31+
BindWith(interface{}, binder.Binder) error
32+
BindParams(interface{}) error
33+
BindHeader(interface{}) error
34+
2935
XML(int, interface{}) error
3036
JSON(int, interface{}) error
3137
JSONP(int, string, interface{}) error
3238
HTML(int, string) error
3339
String(int, string, ...interface{}) error
3440
Blob(int, string, []byte) error
35-
Render(int, render.Renderer) error
36-
RenderHTML(int, string, interface{}) error
41+
Render(int, string, interface{}) error
42+
RenderWith(int, render.Renderer) error
3743
File(string) error
3844

45+
Status(int) error
3946
Redirect(int, string) error
40-
41-
Bind(interface{}) error
42-
BindWith(interface{}, binder.Binder) error
43-
BindQuery(interface{}) error
44-
BindHeader(interface{}) error
4547
}
4648

4749
type context struct {
@@ -86,7 +88,7 @@ func (c *context) Params() map[string]string {
8688
if len(c.route.pnames) == 0 {
8789
return nil
8890
}
89-
params := make(map[string]string)
91+
params := make(Params)
9092
for i, p := range c.route.pnames {
9193
if i < len(c.pvalues) {
9294
params[p.key] = c.pvalues[i]
@@ -122,29 +124,24 @@ func (c *context) BindWith(data interface{}, b binder.Binder) error {
122124
return b.Bind(c.request, data)
123125
}
124126

125-
func (c *context) BindQuery(data interface{}) error {
126-
return c.BindWith(data, binder.Query)
127+
func (c *context) BindParams(data interface{}) error {
128+
return binder.Params.Bind(c.Params(), data)
127129
}
128130

129131
func (c *context) BindHeader(data interface{}) error {
130132
return c.BindWith(data, binder.Header)
131133
}
132134

133-
func (c *context) Render(code int, r render.Renderer) error {
135+
func (c *context) Render(code int, name string, data interface{}) error {
134136
c.response.WriteHeader(code)
135-
return r.Render(c.response)
137+
return c.route.Render(c.response, name, data)
136138
}
137139

138140
func (c *context) RenderWith(code int, r render.Renderer) error {
139141
c.response.WriteHeader(code)
140142
return r.Render(c.response)
141143
}
142144

143-
func (c *context) RenderHTML(code int, name string, data interface{}) error {
144-
c.response.WriteHeader(code)
145-
return c.route.Render(c.response, name, data)
146-
}
147-
148145
func (c *context) Blob(code int, contentType string, data []byte) error {
149146
return render.Blob(c.response, code, contentType, data)
150147
}
@@ -169,6 +166,11 @@ func (c *context) HTML(code int, data string) error {
169166
return render.HTML(c.response, code, data)
170167
}
171168

169+
func (c *context) Status(code int) error {
170+
c.response.WriteHeader(code)
171+
return nil
172+
}
173+
172174
func (c *context) Redirect(code int, url string) error {
173175
if code < 300 || code > 308 {
174176
return nil
@@ -238,3 +240,9 @@ func (c *context) reset(r *http.Request, w http.ResponseWriter) {
238240
c.pvalues = c.pvalues[:0]
239241
c.index = -1
240242
}
243+
244+
func NewContext(r *http.Request, w http.ResponseWriter) Context {
245+
c := &context{response: NewResponse(w)}
246+
c.reset(r, w)
247+
return c
248+
}

0 commit comments

Comments
 (0)