├── .gitignore ├── autorouter.go ├── autorouter_test.go ├── go.mod ├── go.sum └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor -------------------------------------------------------------------------------- /autorouter.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | // parseMethodAndArgs parse method and arguments from request context param "path" 14 | // it will trim both left and right '/' first. 15 | // if path empty, we will treat request http method as method name and arguments empty. 16 | func parseMethodAndArgs(c *gin.Context) (method string, args []string) { 17 | queryPath := c.Param("path") 18 | queryPath = strings.Trim(queryPath, "/") 19 | if queryPath == "" { 20 | return c.Request.Method, nil 21 | } 22 | 23 | segments := strings.Split(queryPath, "/") 24 | method = segments[0] 25 | segments = segments[1:] 26 | 27 | if len(segments) == 0 { 28 | return 29 | } 30 | 31 | return method, segments 32 | } 33 | 34 | // parseRESTMethodAndArgs is like parseMethodAndArgs, but all segments are parsed to arguments. 35 | func parseRESTMethodAndArgs(c *gin.Context) (method string, args []string) { 36 | method = c.Request.Method 37 | path := c.Param("path") 38 | path = strings.Trim(path, "/") 39 | return method, strings.Split(path, "/") 40 | } 41 | 42 | func AutoRoute(handler interface{}) gin.HandlerFunc { 43 | return findAndCall(handler, true, parseMethodAndArgs) 44 | } 45 | 46 | func AutoRouteAny(handler interface{}) gin.HandlerFunc { 47 | return findAndCall(handler, false, parseMethodAndArgs) 48 | } 49 | 50 | func convertType(val string, inType reflect.Kind) (reflect.Value, error) { 51 | switch inType { 52 | case reflect.Int: 53 | tmp, err := strconv.Atoi(val) 54 | if err != nil { 55 | return reflect.Value{}, err 56 | } 57 | return reflect.ValueOf(tmp), nil 58 | case reflect.Int32: 59 | tmp, err := strconv.Atoi(val) 60 | if err != nil { 61 | return reflect.Value{}, err 62 | } 63 | return reflect.ValueOf(int32(tmp)), nil 64 | case reflect.Int64: 65 | tmp, err := strconv.Atoi(val) 66 | if err != nil { 67 | return reflect.Value{}, err 68 | } 69 | return reflect.ValueOf(int64(tmp)), nil 70 | case reflect.Uint: 71 | i, err := strconv.ParseUint(val, 10, 64) 72 | if err != nil { 73 | return reflect.Value{}, err 74 | } 75 | return reflect.ValueOf(uint(i)), nil 76 | case reflect.Uint32: 77 | i, err := strconv.ParseUint(val, 10, 32) 78 | if err != nil { 79 | return reflect.Value{}, err 80 | } 81 | return reflect.ValueOf(uint32(i)), nil 82 | case reflect.Uint64: 83 | i, err := strconv.ParseUint(val, 10, 64) 84 | if err != nil { 85 | return reflect.Value{}, err 86 | } 87 | return reflect.ValueOf(uint64(i)), nil 88 | case reflect.Float32: 89 | f, err := strconv.ParseFloat(val, 32) 90 | if err != nil { 91 | return reflect.Value{}, err 92 | } 93 | return reflect.ValueOf(float32(f)), nil 94 | case reflect.Float64: 95 | f, err := strconv.ParseFloat(val, 64) 96 | if err != nil { 97 | return reflect.Value{}, err 98 | } 99 | return reflect.ValueOf(f), nil 100 | case reflect.String: 101 | return reflect.ValueOf(val), nil 102 | } 103 | 104 | return reflect.Value{}, errors.New("unsupport args type") 105 | } 106 | 107 | var ginContextType = reflect.TypeOf(&gin.Context{}) 108 | 109 | // findHandlerFuncs find valid methods of handler. 110 | // if onlyCtx is true, valid means the method only have one parameter(except receiver) and its type is *gin.Context 111 | // otherwise, valid means the method could have more than one parameters(except receiver too), but the first argument type 112 | // must be *gin.Context, and others arguments type must be basic type, such as any int, any float, or string. 113 | func findHandlerFuncs(handler interface{}, onlyCtx bool) map[string]reflect.Value { 114 | funcs := make(map[string]reflect.Value) 115 | rv := reflect.ValueOf(handler) 116 | rt := reflect.TypeOf(handler) 117 | 118 | for i := 0; i < rt.NumMethod(); i++ { 119 | fn := rv.Method(i) 120 | ft := rt.Method(i) 121 | 122 | if (onlyCtx && ft.Type.NumIn() == 2 && ft.Type.In(1) == ginContextType) || 123 | (!onlyCtx && ft.Type.NumIn() >= 2 && ft.Type.In(1) == ginContextType) { 124 | funcs[strings.ToLower(ft.Name)] = fn 125 | } 126 | } 127 | return funcs 128 | } 129 | 130 | func REST(handler interface{}) gin.HandlerFunc { 131 | return findAndCall(handler, true, parseRESTMethodAndArgs) 132 | } 133 | 134 | func RESTAny(handler interface{}) gin.HandlerFunc { 135 | return findAndCall(handler, false, parseRESTMethodAndArgs) 136 | } 137 | 138 | func findAndCall(handler interface{}, onlyCtx bool, finder func(c *gin.Context) (string, []string)) gin.HandlerFunc { 139 | funcs := findHandlerFuncs(handler, onlyCtx) 140 | return func(c *gin.Context) { 141 | 142 | method, args := finder(c) 143 | fn, exist := funcs[strings.ToLower(method)] 144 | if !exist { 145 | http.NotFound(c.Writer, c.Request) 146 | c.Abort() 147 | return 148 | } 149 | 150 | numIn := fn.Type().NumIn() // include method receiver 151 | if (fn.Type().IsVariadic() && numIn-2 > len(args)) || 152 | numIn-1 > len(args) { // not enough arguments 153 | http.NotFound(c.Writer, c.Request) 154 | c.Abort() 155 | return 156 | } 157 | 158 | arguments := make([]reflect.Value, 1, numIn) 159 | arguments[0] = reflect.ValueOf(c) // *gin.Context 160 | 161 | if !onlyCtx { 162 | t := numIn - 1 // non-variadic arguments number 163 | isVariadic := fn.Type().IsVariadic() 164 | if isVariadic { 165 | t-- 166 | } 167 | 168 | popArg := func() (string, bool) { 169 | if len(args) > 0 { 170 | t := args[0] 171 | args = args[1:] 172 | return t, true 173 | } 174 | return "", false 175 | } 176 | for i := 0; i < t; i++ { 177 | argStr, ok := popArg() 178 | if !ok { 179 | break 180 | } 181 | arg, err := convertType(argStr, fn.Type().In(i+1).Kind()) 182 | if err != nil { 183 | http.NotFound(c.Writer, c.Request) 184 | c.Abort() 185 | return 186 | } 187 | arguments = append(arguments, arg) 188 | } 189 | 190 | if isVariadic { 191 | if len(args) > 0 { 192 | variadicKind := fn.Type().In(numIn - 1).Elem().Kind() 193 | for { 194 | argStr, ok := popArg() 195 | if !ok { 196 | break 197 | } 198 | arg, err := convertType(argStr, variadicKind) 199 | if err != nil { 200 | http.NotFound(c.Writer, c.Request) 201 | c.Abort() 202 | return 203 | } 204 | arguments = append(arguments, arg) 205 | } 206 | } 207 | } 208 | } 209 | 210 | fn.Call(arguments) 211 | 212 | } 213 | 214 | } 215 | -------------------------------------------------------------------------------- /autorouter_test.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "strconv" 8 | "testing" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | type T struct { 14 | } 15 | 16 | func (t *T) Get(c *gin.Context) { 17 | c.Writer.WriteString("hello from T.Get") 18 | } 19 | 20 | func (t *T) Post(c *gin.Context) { 21 | c.Writer.WriteString("hello from T.Post") 22 | } 23 | 24 | func (t *T) Delete(c *gin.Context, id int) { 25 | fmt.Println("param id:", c.Param("id")) 26 | fmt.Println("arg id:", id) 27 | c.Writer.WriteString("hello from T.Delete") 28 | } 29 | 30 | func (t *T) Hello(c *gin.Context, name string) { 31 | c.Writer.WriteString("hello " + name) 32 | } 33 | 34 | func (t *T) AddInt(c *gin.Context, a ...int) { 35 | var sum int 36 | for _, v := range a { 37 | sum += v 38 | } 39 | c.Writer.WriteString(strconv.Itoa(sum)) 40 | } 41 | 42 | func ExampleAutoRouter() { 43 | gin.SetMode(gin.ReleaseMode) 44 | r := gin.New() 45 | 46 | //r.Any("/*path", AutoRoute(&T{})) 47 | r.Any("/*path", REST(&T{})) 48 | r.Run(":9091") 49 | 50 | } 51 | 52 | func ExampleRouteAny() { 53 | gin.SetMode(gin.ReleaseMode) 54 | r := gin.New() 55 | 56 | //r.Any("/article/*path", AutoRouteAny(&T{})) 57 | r.Any("/article/*path", RESTAny(&T{})) 58 | r.Run(":9092") 59 | } 60 | 61 | func TestFindHandlerFuncs(t *testing.T) { 62 | funcs := findHandlerFuncs(&T{}, true) 63 | if len(funcs) != 2 { 64 | t.Fatal("wrong func number") 65 | } 66 | exist := func(name string) bool { 67 | _, ok := funcs[name] 68 | return ok 69 | } 70 | if !exist("get") || !exist("post") { 71 | t.Fatal("wrong funcs") 72 | } 73 | 74 | funcs = findHandlerFuncs(&T{}, false) 75 | if len(funcs) != 5 { 76 | t.Fatal("wrong funcs number") 77 | } 78 | if !exist("addint") || !exist("hello") { 79 | t.Fatal("wrong funcs") 80 | } 81 | } 82 | 83 | func TestParseMethodAndArgs(t *testing.T) { 84 | var ( 85 | method string 86 | args []string 87 | ) 88 | c := &gin.Context{ 89 | Request: &http.Request{ 90 | Method: http.MethodPost, 91 | }, 92 | Params: gin.Params{ 93 | { 94 | Key: "path", 95 | Value: "", 96 | }, 97 | }, 98 | } 99 | 100 | setpath := func(path string) { 101 | c.Params[0].Value = path 102 | } 103 | 104 | method, args = parseMethodAndArgs(c) 105 | if method != http.MethodPost || len(args) > 0 { 106 | t.Fatal("method or args error") 107 | } 108 | 109 | setpath("/") 110 | method, args = parseMethodAndArgs(c) 111 | if method != http.MethodPost || len(args) > 0 { 112 | t.Fatal("method or args error") 113 | } 114 | 115 | setpath("/foo/bar/") 116 | method, args = parseMethodAndArgs(c) 117 | if method != "foo" || len(args) != 1 || args[0] != "bar" { 118 | t.Fatal("method or args error") 119 | } 120 | 121 | } 122 | 123 | type T2 struct{} 124 | 125 | func (t *T2) F1(c *gin.Context) { c.Writer.WriteString("ok") } 126 | func (t *T2) F2(c *gin.Context) { c.Writer.WriteString("ok") } 127 | func (t *T2) F3(c *gin.Context) { c.Writer.WriteString("ok") } 128 | 129 | func BenchmarkManualRoute(b *testing.B) { 130 | gin.SetMode(gin.ReleaseMode) 131 | r := gin.New() 132 | 133 | t := &T2{} 134 | r.GET("/f1", t.F1) 135 | r.GET("/f2", t.F2) 136 | r.GET("/f3", t.F3) 137 | 138 | for i := 0; i < b.N; i++ { 139 | req := httptest.NewRequest(http.MethodGet, "/f1", nil) 140 | rr := httptest.NewRecorder() 141 | r.ServeHTTP(rr, req) 142 | } 143 | 144 | } 145 | 146 | func BenchmarkAutoRoute(b *testing.B) { 147 | gin.SetMode(gin.ReleaseMode) 148 | r := gin.New() 149 | 150 | r.Any("/:path", AutoRoute(&T2{})) 151 | for i := 0; i < b.N; i++ { 152 | req := httptest.NewRequest(http.MethodGet, "/f1", nil) 153 | rr := httptest.NewRecorder() 154 | r.ServeHTTP(rr, req) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/chenqinghe/gin-autorouter 2 | 3 | go 1.13 4 | 5 | require github.com/gin-gonic/gin v1.7.7 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 5 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 6 | github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= 7 | github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= 8 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 9 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 10 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 11 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 12 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 13 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 14 | github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= 15 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 16 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= 17 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 18 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 19 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 20 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 21 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 22 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 23 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 24 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 25 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 26 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 27 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 28 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 29 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 30 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 31 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 32 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 33 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 34 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 35 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 36 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 37 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 38 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 39 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 40 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 41 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 42 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 43 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 44 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 45 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= 46 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 47 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 48 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 49 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 50 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 51 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 52 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 53 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 54 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 55 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # gin-autorouter 2 | 3 | gin-autorouter is a middleware which could automatic mapping request url to a handler method. 4 | 5 | ## Api 6 | the project have four main functions: 7 | 8 | * AutoRoute 9 | * RouteAny 10 | * REST 11 | * RESTAny 12 | 13 | 14 | ## Basic Uage 15 | 16 | ```go 17 | package main 18 | 19 | type T struct { 20 | } 21 | 22 | func (t *T) Greet(c *gin.Context) { 23 | c.Writer.WriteString("hello from *T.Greet") 24 | } 25 | 26 | func (t *T) Hello(c *gin.Context) { 27 | c.Writer.WriteString("hello from *T.Hello") 28 | } 29 | 30 | func main() { 31 | r := gin.Default() 32 | r.Any("/*path", router.AutoRoute(&T{})) 33 | r.Run(":8080") 34 | } 35 | ``` 36 | you only need to register a router with pattern "/*path" 37 | 38 | view http://localhost:8080/greet, you could see "hello from *T.Greet" 39 | 40 | view http://localhost:8080/hello, you will see "hello from *T.Hello" 41 | 42 | ## RESTful Api 43 | with AutoRouter, you can create restful api very easily. 44 | 45 | ```go 46 | package main 47 | 48 | import "net/http" 49 | 50 | func (h *Article) Get(c *gin.Context) { 51 | articleId := c.Param("id") 52 | // search artile stuff..... 53 | article := model.SearchArticle(articleId) 54 | c.JSONP(http.StatusOK, article) 55 | } 56 | 57 | func (h *Article) Delete(c *gin.Context) { 58 | articleId := c.Param("id") 59 | model.DeleteArticle(articleId) 60 | c.JSONP(http.StatusOK, "ok") 61 | } 62 | 63 | func main() { 64 | r := gin.Default() 65 | r.Any("/article/:id", router.REST(&Article{})) 66 | } 67 | 68 | ``` 69 | also, you can use RESTAny, things will be extremely easy!! 70 | 71 | ```go 72 | package main 73 | 74 | import ( 75 | "fmt" 76 | "net/http" 77 | ) 78 | 79 | func (h *Article) Get(c *gin.Context, id int) { 80 | fmt.Println("article:", id) // output: article: 123 81 | article := model.SearchArticle(id) 82 | c.JSONP(http.StatusOK, article) 83 | } 84 | 85 | func (h *Article) Delete(c *gin.Context, id int) { 86 | fmt.Println("article:", id) // output: article: 123 87 | model.DeleteArticle(id) 88 | c.JSONP(http.StatusOK, "ok") 89 | } 90 | 91 | func main() { 92 | r := gin.Default() 93 | r.Any("/article/:path", router.RESTAny(&Article{})) 94 | } 95 | 96 | // GET /article/123 => *Article.Get(c, 123) 97 | // DELETE /article/123 => *Article.Delete(c, 123) 98 | 99 | ``` 100 | 101 | ## Mapping Rules 102 | the mapping is basic on *gin.Context.Param("path"). path will be exploded to several segments by '/' 103 | * if path is empty, method is request http method 104 | * the first segment is method 105 | * others will be method arguments 106 | * segments number MUST be equal or greater than method arguments number 107 | * if method is variadic, the segments mapping to last argument could be zero 108 | * otherwise, "404 not found" will be returned 109 | 110 | 111 | some examples: 112 |
path | 115 |method | 116 |arguments(exclude *gin.Context) | 117 |
/ | 120 |REQUEST METHOD | 121 |nil | 122 |
/foo | 125 |foo | 126 |nil | 127 |
/foo/bar | 130 |foo | 131 |[bar] | 132 |
/foo/bar/123 | 135 |foo | 136 |[bar,123] | 137 |