add /*filepath route support

This commit is contained in:
gzdaijie
2019-08-18 16:34:49 +08:00
parent e0d4cde290
commit e685bc0ff9
16 changed files with 267 additions and 111 deletions
+33 -22
View File
@@ -17,8 +17,25 @@ func newRouter() *router {
}
}
// Only one * is allowed
func parsePattern(pattern string) []string {
vs := strings.Split(pattern, "/")
parts := make([]string, 0)
for _, item := range vs {
if item != "" {
parts = append(parts, item)
if item[0] == '*' {
break
}
}
}
return parts
}
func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
parts := filterNonEmpty(strings.Split(pattern, "/"))
parts := parsePattern(pattern)
key := method + "-" + pattern
_, ok := r.roots[method]
if !ok {
@@ -28,19 +45,8 @@ func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
r.handlers[key] = handler
}
func (r *router) handle(c *Context) {
n, params := r.getRoute(c.Method, c.Path)
if n != nil {
c.Params = params
key := c.Method + "-" + n.pattern
r.handlers[key](c)
} else {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
}
}
func (r *router) getRoute(method string, pattern string) (*node, map[string]string) {
searchParts := filterNonEmpty(strings.Split(pattern, "/"))
func (r *router) getRoute(method string, path string) (*node, map[string]string) {
searchParts := parsePattern(path)
params := make(map[string]string)
root, ok := r.roots[method]
@@ -51,11 +57,15 @@ func (r *router) getRoute(method string, pattern string) (*node, map[string]stri
n := root.search(searchParts, 0)
if n != nil {
parts := filterNonEmpty(strings.Split(n.pattern, "/"))
parts := parsePattern(n.pattern)
for index, part := range parts {
if part[0] == ':' {
params[part[1:]] = searchParts[index]
}
if part[0] == '*' && len(part) > 1 {
params[part[1:]] = strings.Join(searchParts[index:], "/")
break
}
}
return n, params
}
@@ -73,12 +83,13 @@ func (r *router) getRoutes(method string) []*node {
return nodes
}
func filterNonEmpty(vs []string) []string {
parts := make([]string, 0)
for _, item := range vs {
if item != "" {
parts = append(parts, item)
}
func (r *router) handle(c *Context) {
n, params := r.getRoute(c.Method, c.Path)
if n != nil {
c.Params = params
key := c.Method + "-" + n.pattern
r.handlers[key](c)
} else {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
}
return parts
}
+28 -1
View File
@@ -2,6 +2,7 @@ package gee
import (
"fmt"
"reflect"
"testing"
)
@@ -11,9 +12,19 @@ func newTestRouter() *router {
r.addRoute("GET", "/hello/:name", nil)
r.addRoute("GET", "/hello/b/c", nil)
r.addRoute("GET", "/hi/:name", nil)
r.addRoute("GET", "/assets/*filepath", nil)
return r
}
func TestParsePattern(t *testing.T) {
ok := reflect.DeepEqual(parsePattern("/p/:name"), []string{"p", ":name"})
ok = ok && reflect.DeepEqual(parsePattern("/p/*"), []string{"p", "*"})
ok = ok && reflect.DeepEqual(parsePattern("/p/*name/*"), []string{"p", "*name"})
if !ok {
t.Fatal("test parsePattern failed")
}
}
func TestGetRoute(t *testing.T) {
r := newTestRouter()
n, ps := r.getRoute("GET", "/hello/geektutu")
@@ -34,6 +45,22 @@ func TestGetRoute(t *testing.T) {
}
func TestGetRoute2(t *testing.T) {
r := newTestRouter()
n1, ps1 := r.getRoute("GET", "/assets/file1.txt")
ok1 := n1.pattern == "/assets/*filepath" && ps1["filepath"] == "file1.txt"
if !ok1 {
t.Fatal("pattern shoule be /assets/*filepath & filepath shoule be file1.txt")
}
n2, ps2 := r.getRoute("GET", "/assets/css/test.css")
ok2 := n2.pattern == "/assets/*filepath" && ps2["filepath"] == "css/test.css"
if !ok2 {
t.Fatal("pattern shoule be /assets/*filepath & filepath shoule be css/test.css")
}
}
func TestGetRoutes(t *testing.T) {
r := newTestRouter()
nodes := r.getRoutes("GET")
@@ -41,7 +68,7 @@ func TestGetRoutes(t *testing.T) {
fmt.Println(i+1, n)
}
if len(nodes) != 4 {
if len(nodes) != 5 {
t.Fatal("the number of routes shoule be 4")
}
}
+3 -2
View File
@@ -2,6 +2,7 @@ package gee
import (
"fmt"
"strings"
)
type node struct {
@@ -24,14 +25,14 @@ func (n *node) insert(pattern string, parts []string, height int) {
part := parts[height]
child := n.matchChild(part)
if child == nil {
child = &node{part: part, isWild: part[0] == ':'}
child = &node{part: part, isWild: part[0] == ':' || part[0] == '*'}
n.children = append(n.children, child)
}
child.insert(pattern, parts, height+1)
}
func (n *node) search(parts []string, height int) *node {
if len(parts) == height {
if len(parts) == height || strings.HasPrefix(n.part, "*") {
if n.pattern == "" {
return nil
}
+33 -22
View File
@@ -17,8 +17,25 @@ func newRouter() *router {
}
}
// Only one * is allowed
func parsePattern(pattern string) []string {
vs := strings.Split(pattern, "/")
parts := make([]string, 0)
for _, item := range vs {
if item != "" {
parts = append(parts, item)
if item[0] == '*' {
break
}
}
}
return parts
}
func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
parts := filterNonEmpty(strings.Split(pattern, "/"))
parts := parsePattern(pattern)
key := method + "-" + pattern
_, ok := r.roots[method]
if !ok {
@@ -28,19 +45,8 @@ func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
r.handlers[key] = handler
}
func (r *router) handle(c *Context) {
n, params := r.getRoute(c.Method, c.Path)
if n != nil {
c.Params = params
key := c.Method + "-" + n.pattern
r.handlers[key](c)
} else {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
}
}
func (r *router) getRoute(method string, pattern string) (*node, map[string]string) {
searchParts := filterNonEmpty(strings.Split(pattern, "/"))
func (r *router) getRoute(method string, path string) (*node, map[string]string) {
searchParts := parsePattern(path)
params := make(map[string]string)
root, ok := r.roots[method]
@@ -51,11 +57,15 @@ func (r *router) getRoute(method string, pattern string) (*node, map[string]stri
n := root.search(searchParts, 0)
if n != nil {
parts := filterNonEmpty(strings.Split(n.pattern, "/"))
parts := parsePattern(n.pattern)
for index, part := range parts {
if part[0] == ':' {
params[part[1:]] = searchParts[index]
}
if part[0] == '*' && len(part) > 1 {
params[part[1:]] = strings.Join(searchParts[index:], "/")
break
}
}
return n, params
}
@@ -73,12 +83,13 @@ func (r *router) getRoutes(method string) []*node {
return nodes
}
func filterNonEmpty(vs []string) []string {
parts := make([]string, 0)
for _, item := range vs {
if item != "" {
parts = append(parts, item)
}
func (r *router) handle(c *Context) {
n, params := r.getRoute(c.Method, c.Path)
if n != nil {
c.Params = params
key := c.Method + "-" + n.pattern
r.handlers[key](c)
} else {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
}
return parts
}
+28 -1
View File
@@ -2,6 +2,7 @@ package gee
import (
"fmt"
"reflect"
"testing"
)
@@ -11,9 +12,19 @@ func newTestRouter() *router {
r.addRoute("GET", "/hello/:name", nil)
r.addRoute("GET", "/hello/b/c", nil)
r.addRoute("GET", "/hi/:name", nil)
r.addRoute("GET", "/assets/*filepath", nil)
return r
}
func TestParsePattern(t *testing.T) {
ok := reflect.DeepEqual(parsePattern("/p/:name"), []string{"p", ":name"})
ok = ok && reflect.DeepEqual(parsePattern("/p/*"), []string{"p", "*"})
ok = ok && reflect.DeepEqual(parsePattern("/p/*name/*"), []string{"p", "*name"})
if !ok {
t.Fatal("test parsePattern failed")
}
}
func TestGetRoute(t *testing.T) {
r := newTestRouter()
n, ps := r.getRoute("GET", "/hello/geektutu")
@@ -34,6 +45,22 @@ func TestGetRoute(t *testing.T) {
}
func TestGetRoute2(t *testing.T) {
r := newTestRouter()
n1, ps1 := r.getRoute("GET", "/assets/file1.txt")
ok1 := n1.pattern == "/assets/*filepath" && ps1["filepath"] == "file1.txt"
if !ok1 {
t.Fatal("pattern shoule be /assets/*filepath & filepath shoule be file1.txt")
}
n2, ps2 := r.getRoute("GET", "/assets/css/test.css")
ok2 := n2.pattern == "/assets/*filepath" && ps2["filepath"] == "css/test.css"
if !ok2 {
t.Fatal("pattern shoule be /assets/*filepath & filepath shoule be css/test.css")
}
}
func TestGetRoutes(t *testing.T) {
r := newTestRouter()
nodes := r.getRoutes("GET")
@@ -41,7 +68,7 @@ func TestGetRoutes(t *testing.T) {
fmt.Println(i+1, n)
}
if len(nodes) != 4 {
if len(nodes) != 5 {
t.Fatal("the number of routes shoule be 4")
}
}
+3 -2
View File
@@ -2,6 +2,7 @@ package gee
import (
"fmt"
"strings"
)
type node struct {
@@ -24,14 +25,14 @@ func (n *node) insert(pattern string, parts []string, height int) {
part := parts[height]
child := n.matchChild(part)
if child == nil {
child = &node{part: part, isWild: part[0] == ':'}
child = &node{part: part, isWild: part[0] == ':' || part[0] == '*'}
n.children = append(n.children, child)
}
child.insert(pattern, parts, height+1)
}
func (n *node) search(parts []string, height int) *node {
if len(parts) == height {
if len(parts) == height || strings.HasPrefix(n.part, "*") {
if n.pattern == "" {
return nil
}
+37 -26
View File
@@ -17,8 +17,25 @@ func newRouter() *router {
}
}
// Only one * is allowed
func parsePattern(pattern string) []string {
vs := strings.Split(pattern, "/")
parts := make([]string, 0)
for _, item := range vs {
if item != "" {
parts = append(parts, item)
if item[0] == '*' {
break
}
}
}
return parts
}
func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
parts := filterNonEmpty(strings.Split(pattern, "/"))
parts := parsePattern(pattern)
key := method + "-" + pattern
_, ok := r.roots[method]
if !ok {
@@ -28,23 +45,8 @@ func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
r.handlers[key] = handler
}
func (r *router) handle(c *Context) {
n, params := r.getRoute(c.Method, c.Path)
if n != nil {
key := c.Method + "-" + n.pattern
c.Params = params
c.handlers = append(c.handlers, r.handlers[key])
} else {
c.handlers = append(c.handlers, func(c *Context) {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
})
}
c.Next()
}
func (r *router) getRoute(method string, pattern string) (*node, map[string]string) {
searchParts := filterNonEmpty(strings.Split(pattern, "/"))
func (r *router) getRoute(method string, path string) (*node, map[string]string) {
searchParts := parsePattern(path)
params := make(map[string]string)
root, ok := r.roots[method]
@@ -55,11 +57,15 @@ func (r *router) getRoute(method string, pattern string) (*node, map[string]stri
n := root.search(searchParts, 0)
if n != nil {
parts := filterNonEmpty(strings.Split(n.pattern, "/"))
parts := parsePattern(n.pattern)
for index, part := range parts {
if part[0] == ':' {
params[part[1:]] = searchParts[index]
}
if part[0] == '*' && len(part) > 1 {
params[part[1:]] = strings.Join(searchParts[index:], "/")
break
}
}
return n, params
}
@@ -77,12 +83,17 @@ func (r *router) getRoutes(method string) []*node {
return nodes
}
func filterNonEmpty(vs []string) []string {
parts := make([]string, 0)
for _, item := range vs {
if item != "" {
parts = append(parts, item)
}
func (r *router) handle(c *Context) {
n, params := r.getRoute(c.Method, c.Path)
if n != nil {
key := c.Method + "-" + n.pattern
c.Params = params
c.handlers = append(c.handlers, r.handlers[key])
} else {
c.handlers = append(c.handlers, func(c *Context) {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
})
}
return parts
c.Next()
}
+28 -1
View File
@@ -2,6 +2,7 @@ package gee
import (
"fmt"
"reflect"
"testing"
)
@@ -11,9 +12,19 @@ func newTestRouter() *router {
r.addRoute("GET", "/hello/:name", nil)
r.addRoute("GET", "/hello/b/c", nil)
r.addRoute("GET", "/hi/:name", nil)
r.addRoute("GET", "/assets/*filepath", nil)
return r
}
func TestParsePattern(t *testing.T) {
ok := reflect.DeepEqual(parsePattern("/p/:name"), []string{"p", ":name"})
ok = ok && reflect.DeepEqual(parsePattern("/p/*"), []string{"p", "*"})
ok = ok && reflect.DeepEqual(parsePattern("/p/*name/*"), []string{"p", "*name"})
if !ok {
t.Fatal("test parsePattern failed")
}
}
func TestGetRoute(t *testing.T) {
r := newTestRouter()
n, ps := r.getRoute("GET", "/hello/geektutu")
@@ -34,6 +45,22 @@ func TestGetRoute(t *testing.T) {
}
func TestGetRoute2(t *testing.T) {
r := newTestRouter()
n1, ps1 := r.getRoute("GET", "/assets/file1.txt")
ok1 := n1.pattern == "/assets/*filepath" && ps1["filepath"] == "file1.txt"
if !ok1 {
t.Fatal("pattern shoule be /assets/*filepath & filepath shoule be file1.txt")
}
n2, ps2 := r.getRoute("GET", "/assets/css/test.css")
ok2 := n2.pattern == "/assets/*filepath" && ps2["filepath"] == "css/test.css"
if !ok2 {
t.Fatal("pattern shoule be /assets/*filepath & filepath shoule be css/test.css")
}
}
func TestGetRoutes(t *testing.T) {
r := newTestRouter()
nodes := r.getRoutes("GET")
@@ -41,7 +68,7 @@ func TestGetRoutes(t *testing.T) {
fmt.Println(i+1, n)
}
if len(nodes) != 4 {
if len(nodes) != 5 {
t.Fatal("the number of routes shoule be 4")
}
}
+3 -2
View File
@@ -2,6 +2,7 @@ package gee
import (
"fmt"
"strings"
)
type node struct {
@@ -24,14 +25,14 @@ func (n *node) insert(pattern string, parts []string, height int) {
part := parts[height]
child := n.matchChild(part)
if child == nil {
child = &node{part: part, isWild: part[0] == ':'}
child = &node{part: part, isWild: part[0] == ':' || part[0] == '*'}
n.children = append(n.children, child)
}
child.insert(pattern, parts, height+1)
}
func (n *node) search(parts []string, height int) *node {
if len(parts) == height {
if len(parts) == height || strings.HasPrefix(n.part, "*") {
if n.pattern == "" {
return nil
}
+2 -2
View File
@@ -24,7 +24,7 @@ type (
router *router
groups []*RouterGroup // store all group
htmlTemplates *template.Template // for html render
funcMap template.FuncMap
funcMap template.FuncMap // for html render
}
)
@@ -89,7 +89,7 @@ func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileS
// serve static files
func (group *RouterGroup) Static(relativePath string, root string) {
handler := group.createStaticHandler(relativePath, http.Dir(root))
urlPattern := path.Join(relativePath, "/:filepath")
urlPattern := path.Join(relativePath, "/*filepath")
// Register GET handlers
group.GET(urlPattern, handler)
}
+37 -26
View File
@@ -17,8 +17,25 @@ func newRouter() *router {
}
}
// Only one * is allowed
func parsePattern(pattern string) []string {
vs := strings.Split(pattern, "/")
parts := make([]string, 0)
for _, item := range vs {
if item != "" {
parts = append(parts, item)
if item[0] == '*' {
break
}
}
}
return parts
}
func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
parts := filterNonEmpty(strings.Split(pattern, "/"))
parts := parsePattern(pattern)
key := method + "-" + pattern
_, ok := r.roots[method]
if !ok {
@@ -28,23 +45,8 @@ func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
r.handlers[key] = handler
}
func (r *router) handle(c *Context) {
n, params := r.getRoute(c.Method, c.Path)
if n != nil {
key := c.Method + "-" + n.pattern
c.Params = params
c.handlers = append(c.handlers, r.handlers[key])
} else {
c.handlers = append(c.handlers, func(c *Context) {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
})
}
c.Next()
}
func (r *router) getRoute(method string, pattern string) (*node, map[string]string) {
searchParts := filterNonEmpty(strings.Split(pattern, "/"))
func (r *router) getRoute(method string, path string) (*node, map[string]string) {
searchParts := parsePattern(path)
params := make(map[string]string)
root, ok := r.roots[method]
@@ -55,11 +57,15 @@ func (r *router) getRoute(method string, pattern string) (*node, map[string]stri
n := root.search(searchParts, 0)
if n != nil {
parts := filterNonEmpty(strings.Split(n.pattern, "/"))
parts := parsePattern(n.pattern)
for index, part := range parts {
if part[0] == ':' {
params[part[1:]] = searchParts[index]
}
if part[0] == '*' && len(part) > 1 {
params[part[1:]] = strings.Join(searchParts[index:], "/")
break
}
}
return n, params
}
@@ -77,12 +83,17 @@ func (r *router) getRoutes(method string) []*node {
return nodes
}
func filterNonEmpty(vs []string) []string {
parts := make([]string, 0)
for _, item := range vs {
if item != "" {
parts = append(parts, item)
}
func (r *router) handle(c *Context) {
n, params := r.getRoute(c.Method, c.Path)
if n != nil {
key := c.Method + "-" + n.pattern
c.Params = params
c.handlers = append(c.handlers, r.handlers[key])
} else {
c.handlers = append(c.handlers, func(c *Context) {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
})
}
return parts
c.Next()
}
+28 -1
View File
@@ -2,6 +2,7 @@ package gee
import (
"fmt"
"reflect"
"testing"
)
@@ -11,9 +12,19 @@ func newTestRouter() *router {
r.addRoute("GET", "/hello/:name", nil)
r.addRoute("GET", "/hello/b/c", nil)
r.addRoute("GET", "/hi/:name", nil)
r.addRoute("GET", "/assets/*filepath", nil)
return r
}
func TestParsePattern(t *testing.T) {
ok := reflect.DeepEqual(parsePattern("/p/:name"), []string{"p", ":name"})
ok = ok && reflect.DeepEqual(parsePattern("/p/*"), []string{"p", "*"})
ok = ok && reflect.DeepEqual(parsePattern("/p/*name/*"), []string{"p", "*name"})
if !ok {
t.Fatal("test parsePattern failed")
}
}
func TestGetRoute(t *testing.T) {
r := newTestRouter()
n, ps := r.getRoute("GET", "/hello/geektutu")
@@ -34,6 +45,22 @@ func TestGetRoute(t *testing.T) {
}
func TestGetRoute2(t *testing.T) {
r := newTestRouter()
n1, ps1 := r.getRoute("GET", "/assets/file1.txt")
ok1 := n1.pattern == "/assets/*filepath" && ps1["filepath"] == "file1.txt"
if !ok1 {
t.Fatal("pattern shoule be /assets/*filepath & filepath shoule be file1.txt")
}
n2, ps2 := r.getRoute("GET", "/assets/css/test.css")
ok2 := n2.pattern == "/assets/*filepath" && ps2["filepath"] == "css/test.css"
if !ok2 {
t.Fatal("pattern shoule be /assets/*filepath & filepath shoule be css/test.css")
}
}
func TestGetRoutes(t *testing.T) {
r := newTestRouter()
nodes := r.getRoutes("GET")
@@ -41,7 +68,7 @@ func TestGetRoutes(t *testing.T) {
fmt.Println(i+1, n)
}
if len(nodes) != 4 {
if len(nodes) != 5 {
t.Fatal("the number of routes shoule be 4")
}
}
+3 -2
View File
@@ -2,6 +2,7 @@ package gee
import (
"fmt"
"strings"
)
type node struct {
@@ -24,14 +25,14 @@ func (n *node) insert(pattern string, parts []string, height int) {
part := parts[height]
child := n.matchChild(part)
if child == nil {
child = &node{part: part, isWild: part[0] == ':'}
child = &node{part: part, isWild: part[0] == ':' || part[0] == '*'}
n.children = append(n.children, child)
}
child.insert(pattern, parts, height+1)
}
func (n *node) search(parts []string, height int) *node {
if len(parts) == height {
if len(parts) == height || strings.HasPrefix(n.part, "*") {
if n.pattern == "" {
return nil
}
+1 -1
View File
@@ -1,4 +1,4 @@
<html>
<link rel="stylesheet" href="/assets/geektutu.css">
<link rel="stylesheet" href="/assets/css/geektutu.css">
<p>geektutu.css is loaded</p>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB