summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine.go3
-rw-r--r--parser.go55
-rw-r--r--parser_test.go79
-rw-r--r--router.go15
-rw-r--r--router_test.go35
-rw-r--r--types.go1
6 files changed, 154 insertions, 34 deletions
diff --git a/engine.go b/engine.go
index e3c8ca8..f777e6d 100644
--- a/engine.go
+++ b/engine.go
@@ -33,7 +33,7 @@ func (api *Engine) DELETE(path string, h Handler) {
// serve and start
func (a *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
- handler, params := a.router.Find(req.Method, req.URL.Path)
+ handler, params, query := a.router.Find(req.Method, req.URL.Path)
if handler == nil {
http.NotFound(w, req)
return
@@ -42,6 +42,7 @@ func (a *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
W: w,
R: req,
Params: params,
+ Querys: query,
})
}
diff --git a/parser.go b/parser.go
new file mode 100644
index 0000000..87b06d0
--- /dev/null
+++ b/parser.go
@@ -0,0 +1,55 @@
+package growel
+
+import (
+ "fmt"
+ "strings"
+)
+
+func ParsePath(path string) ([]string, map[string]string) {
+ var query_str string
+ path, query_str = extractQueryStr(path)
+
+ parts := splitPath(path)
+
+ if len(parts) == 0 {
+ return parts, map[string]string{}
+ }
+
+ query := parseQueryStr(query_str)
+
+ return parts, query
+}
+
+func splitPath(path string) []string {
+ return strings.Split(strings.Trim(path, "/"), "/")
+}
+
+func extractQueryStr(str string) (path string, querystr string) {
+ str_slice := strings.Split(str, "?")
+ leng := len(str_slice)
+ if leng > 2 {
+ fmt.Println("Error parsing path: multiple '?' found.")
+ // return anyway
+ } else if leng == 1 {
+ // no query string found
+ return str_slice[0], ""
+ }
+ return str_slice[0], str_slice[1]
+}
+
+func parseQueryStr(str string) map[string]string {
+ query := make(map[string]string)
+ if str == "" {
+ return query
+ }
+ pairs := strings.Split(str, "&")
+ for _, pair := range pairs {
+ kv := strings.Split(pair, "=")
+ if len(kv) != 2 {
+ fmt.Println("Error parsing query string: invalid key-value pair.")
+ continue
+ }
+ query[kv[0]] = kv[1]
+ }
+ return query
+}
diff --git a/parser_test.go b/parser_test.go
new file mode 100644
index 0000000..c63f758
--- /dev/null
+++ b/parser_test.go
@@ -0,0 +1,79 @@
+package growel
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+)
+
+func TestRouterSplitPath(t *testing.T) {
+ tests := []struct {
+ input string
+ want []string
+ }{
+ {"/", []string{""}},
+ {"/hello", []string{"hello"}},
+ {"/hello/world", []string{"hello", "world"}},
+ {"//hello//world//", []string{"hello", "", "world"}},
+ {"/hello?world=true", []string{"hello?world=true"}},
+ }
+
+ for _, tt := range tests {
+ got := splitPath(tt.input)
+ if len(got) != len(tt.want) {
+ t.Fatalf("splitPath(%q) length mismatch: got %v want %v", tt.input, got, tt.want)
+ }
+ for i := range got {
+ if got[i] != tt.want[i] {
+ t.Fatalf("splitPath(%q) mismatch: got %v want %v", tt.input, got, tt.want)
+ }
+ }
+ }
+}
+
+func TestExtractQueryStr(t *testing.T) {
+ tests := []struct {
+ input string
+ want1 string
+ want2 string
+ }{
+ {"", "", ""},
+ {"hello", "hello", ""},
+ {"user/23/", "user/23/", ""},
+ {"api?str=123&yrs=23", "api", "str=123&yrs=23"},
+ {"w?st?r", "w", "st"},
+ {"w?str", "w", "str"},
+ }
+
+ for _, tt := range tests {
+ got1, got2 := extractQueryStr(tt.input)
+ if got1 != tt.want1 && got2 != tt.want2 {
+ fmt.Printf("want1: %s, got1: %s\n", tt.want1, got1)
+ fmt.Printf("want2: %s, got2: %s\n", tt.want2, got2)
+ t.Fail()
+ }
+ }
+}
+
+func TestParseQueryStr(t *testing.T) {
+ tests := []struct {
+ input string
+ want map[string]string
+ }{
+ {"str=123&yrs=23", map[string]string{
+ "str": "123",
+ "yrs": "23",
+ }},
+ {"str=abc", map[string]string{
+ "str": "abc",
+ }},
+ }
+
+ for _, tt := range tests {
+ got := parseQueryStr(tt.input)
+ if !reflect.DeepEqual(got, tt.want) {
+ fmt.Printf("want: %s, got: %s\n", tt.want, got)
+ t.Fail()
+ }
+ }
+}
diff --git a/router.go b/router.go
index 961230a..d17d132 100644
--- a/router.go
+++ b/router.go
@@ -8,17 +8,14 @@ func NewRouter() *Router {
return &Router{}
}
-func (r *Router) splitPath(path string) []string {
- return strings.Split(strings.Trim(path, "/"), "/")
-}
-
func (r *Router) Add(method string, path string, h Handler) {
- parts := r.splitPath(path)
+ parts, _ := ParsePath(path)
r.routes = append(r.routes, Route{method, parts, h})
}
-func (r *Router) Find(method string, path string) (Handler, map[string]string) {
- parts := r.splitPath(path)
+func (r *Router) Find(method string, path string) (
+ handler Handler, params map[string]string, query map[string]string) {
+ parts, query := ParsePath(path)
for _, rt := range r.routes {
// match HTTP method
@@ -49,8 +46,8 @@ func (r *Router) Find(method string, path string) (Handler, map[string]string) {
}
}
if matched {
- return rt.handler, params
+ return rt.handler, params, query
}
}
- return nil, nil
+ return nil, nil, nil
}
diff --git a/router_test.go b/router_test.go
index 24a2faa..e03d16f 100644
--- a/router_test.go
+++ b/router_test.go
@@ -10,7 +10,7 @@ func TestRouterStaticMatch(t *testing.T) {
r := NewRouter()
r.Add("GET", "/hello", dummy)
- h, params := r.Find("GET", "/hello")
+ h, params, _ := r.Find("GET", "/hello")
if h == nil {
t.Fatal("expected handler, got nil")
}
@@ -23,7 +23,7 @@ func TestRouterStaticNoMatch(t *testing.T) {
r := NewRouter()
r.Add("GET", "/hello", dummy)
- h, _ := r.Find("GET", "/nothello")
+ h, _, _ := r.Find("GET", "/nothello")
if h != nil {
t.Fatal("expected nil handler, got non-nil")
}
@@ -33,7 +33,7 @@ func TestRouterParamMatch(t *testing.T) {
r := NewRouter()
r.Add("GET", "/user/:id", dummy)
- h, params := r.Find("GET", "/user/42")
+ h, params, _ := r.Find("GET", "/user/42")
if h == nil {
t.Fatal("expected handler, got nil")
}
@@ -46,34 +46,21 @@ func TestRouterParamMismatch(t *testing.T) {
r := NewRouter()
r.Add("GET", "/user/:id", dummy)
- h, _ := r.Find("GET", "/user")
+ h, _, _ := r.Find("GET", "/user")
if h != nil {
t.Fatal("expected nil handler, got non-nil")
}
}
-func TestRouterSplitPath(t *testing.T) {
+func TestRouterQueryMatch(t *testing.T) {
r := NewRouter()
+ r.Add("GET", "/user", dummy)
- tests := []struct {
- input string
- want []string
- }{
- {"/", []string{""}},
- {"/hello", []string{"hello"}},
- {"/hello/world", []string{"hello", "world"}},
- {"//hello//world//", []string{"hello", "", "world"}},
+ h, _, query := r.Find("GET", "/user?id=42")
+ if h == nil {
+ t.Fatal("expected handler, got nil")
}
-
- for _, tt := range tests {
- got := r.splitPath(tt.input)
- if len(got) != len(tt.want) {
- t.Fatalf("splitPath(%q) length mismatch: got %v want %v", tt.input, got, tt.want)
- }
- for i := range got {
- if got[i] != tt.want[i] {
- t.Fatalf("splitPath(%q) mismatch: got %v want %v", tt.input, got, tt.want)
- }
- }
+ if query["id"] != "42" {
+ t.Fatalf("expected id=42, got %v", query)
}
}
diff --git a/types.go b/types.go
index e785f4c..45bdf2c 100644
--- a/types.go
+++ b/types.go
@@ -6,6 +6,7 @@ type Context struct {
W http.ResponseWriter
R *http.Request
Params map[string]string
+ Querys map[string]string
}
type Handler func(c *Context)