mirror of
https://github.com/kataras/iris.git
synced 2025-12-17 18:07:01 +00:00
new feature: handle different param types in the exact same path pattern
implements https://github.com/kataras/iris/issues/1315 Former-commit-id: 3e9276f2a95d6fc7c10fbf91186d041dcba72611
This commit is contained in:
16
HISTORY.md
16
HISTORY.md
@@ -19,15 +19,16 @@
|
||||
|
||||
Developers are not forced to upgrade if they don't really need it. Upgrade whenever you feel ready.
|
||||
|
||||
**How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris@v11.2.0`.
|
||||
**How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris@master`.
|
||||
|
||||
# Tu, 30 July 2019 | v11.2.3
|
||||
|
||||
# We, 24 July 2019 | v11.2.1
|
||||
TODO:
|
||||
|
||||
- https://github.com/kataras/iris/issues/1298
|
||||
- https://github.com/kataras/iris/issues/1207
|
||||
- Different parameter types in the same path (done).
|
||||
- [Content negotiation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation) (in-progress)
|
||||
|
||||
## v11.2.2
|
||||
# We, 24 July 2019 | v11.2.2
|
||||
|
||||
Sessions as middleware:
|
||||
|
||||
@@ -47,6 +48,11 @@ app.Get("/path", func(ctx iris.Context){
|
||||
- Add `Session.Len() int` to return the total number of stored values/entries.
|
||||
- Make `Context.HTML` and `Context.Text` to accept an optional, variadic, `args ...interface{}` input arg(s) too.
|
||||
|
||||
## v11.1.1
|
||||
|
||||
- https://github.com/kataras/iris/issues/1298
|
||||
- https://github.com/kataras/iris/issues/1207
|
||||
|
||||
# Tu, 23 July 2019 | v11.2.0
|
||||
|
||||
Read about the new release at: https://dev.to/kataras/iris-version-11-2-released-22bc
|
||||
|
||||
@@ -13,7 +13,9 @@ func main() {
|
||||
ctx.HTML("<h1> Please click <a href='/debug/pprof'>here</a>")
|
||||
})
|
||||
|
||||
app.Any("/debug/pprof/{action:path}", pprof.New())
|
||||
p := pprof.New()
|
||||
app.Any("/debug/pprof", p)
|
||||
app.Any("/debug/pprof/{action:path}", p)
|
||||
// ___________
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
// registers a custom handler for 404 not found http (error) status code,
|
||||
// fires when route not found or manually by ctx.StatusCode(iris.StatusNotFound).
|
||||
@@ -26,6 +27,42 @@ func main() {
|
||||
ctx.Writef(`Same as app.Handle("GET", "/", [...])`)
|
||||
})
|
||||
|
||||
// Different path parameters types in the same path.
|
||||
app.Get("/u/{username:string}", func(ctx iris.Context) {
|
||||
ctx.Writef("before username (string), current route name: %s\n", ctx.RouteName())
|
||||
ctx.Next()
|
||||
}, func(ctx iris.Context) {
|
||||
ctx.Writef("username (string): %s", ctx.Params().Get("username"))
|
||||
})
|
||||
|
||||
app.Get("/u/{id:int}", func(ctx iris.Context) {
|
||||
ctx.Writef("before id (int), current route name: %s\n", ctx.RouteName())
|
||||
ctx.Next()
|
||||
}, func(ctx iris.Context) {
|
||||
ctx.Writef("id (int): %d", ctx.Params().GetIntDefault("id", 0))
|
||||
})
|
||||
|
||||
app.Get("/u/{uid:uint}", func(ctx iris.Context) {
|
||||
ctx.Writef("before uid (uint), current route name: %s\n", ctx.RouteName())
|
||||
ctx.Next()
|
||||
}, func(ctx iris.Context) {
|
||||
ctx.Writef("uid (uint): %d", ctx.Params().GetUintDefault("uid", 0))
|
||||
})
|
||||
|
||||
app.Get("/u/{firstname:alphabetical}", func(ctx iris.Context) {
|
||||
ctx.Writef("before firstname (alphabetical), current route name: %s\n", ctx.RouteName())
|
||||
ctx.Next()
|
||||
}, func(ctx iris.Context) {
|
||||
ctx.Writef("firstname (alphabetical): %s", ctx.Params().Get("firstname"))
|
||||
})
|
||||
|
||||
/*
|
||||
/u/abcd maps to :alphabetical (if :alphabetical registered otherwise :string)
|
||||
/u/42 maps to :uint (if :uint registered otherwise :int)
|
||||
/u/-1 maps to :int (if :int registered otherwise :string)
|
||||
/u/abcd123 maps to :string
|
||||
*/
|
||||
|
||||
app.Get("/donate", donateHandler, donateFinishHandler)
|
||||
|
||||
// Pssst, don't forget dynamic-path example for more "magic"!
|
||||
@@ -128,6 +165,11 @@ func main() {
|
||||
// http://localhost:8080/api/users/blabla
|
||||
// http://localhost:8080/wontfound
|
||||
//
|
||||
// http://localhost:8080/u/abcd
|
||||
// http://localhost:8080/u/42
|
||||
// http://localhost:8080/u/-1
|
||||
// http://localhost:8080/u/abcd123
|
||||
//
|
||||
// if hosts edited:
|
||||
// http://v1.localhost:8080
|
||||
// http://v1.localhost:8080/api/users
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/macro"
|
||||
macroHandler "github.com/kataras/iris/macro/handler"
|
||||
)
|
||||
|
||||
// MethodNone is a Virtual method
|
||||
@@ -109,6 +110,20 @@ func (repo *repository) get(routeName string) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *repository) getRelative(r *Route) *Route {
|
||||
if r.tmpl.IsTrailing() || !macroHandler.CanMakeHandler(r.tmpl) {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, route := range repo.routes {
|
||||
if r.Subdomain == route.Subdomain && r.Method == route.Method && r.FormattedPath == route.FormattedPath && !route.tmpl.IsTrailing() {
|
||||
return route
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *repository) getByPath(tmplPath string) *Route {
|
||||
if repo.pos != nil {
|
||||
if idx, ok := repo.pos[tmplPath]; ok {
|
||||
@@ -345,6 +360,8 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
|
||||
var route *Route // the last one is returned.
|
||||
for _, route = range routes {
|
||||
// global
|
||||
|
||||
route.topLink = api.routes.getRelative(route)
|
||||
api.routes.register(route)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
macroHandler "github.com/kataras/iris/macro/handler"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
)
|
||||
|
||||
// RequestHandler the middle man between acquiring a context and releasing it.
|
||||
@@ -116,29 +117,63 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
||||
})
|
||||
|
||||
for _, r := range registeredRoutes {
|
||||
// build the r.Handlers based on begin and done handlers, if any.
|
||||
r.BuildHandlers()
|
||||
if r.topLink != nil {
|
||||
bindMultiParamTypesHandler(r.topLink, r)
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range registeredRoutes {
|
||||
if r.Subdomain != "" {
|
||||
h.hosts = true
|
||||
}
|
||||
|
||||
// the only "bad" with this is if the user made an error
|
||||
// on route, it will be stacked shown in this build state
|
||||
// and no in the lines of the user's action, they should read
|
||||
// the docs better. Or TODO: add a link here in order to help new users.
|
||||
if err := h.addRoute(r); err != nil {
|
||||
// node errors:
|
||||
rp.Add("%v -> %s", err, r.String())
|
||||
continue
|
||||
if r.topLink == nil {
|
||||
// build the r.Handlers based on begin and done handlers, if any.
|
||||
r.BuildHandlers()
|
||||
|
||||
// the only "bad" with this is if the user made an error
|
||||
// on route, it will be stacked shown in this build state
|
||||
// and no in the lines of the user's action, they should read
|
||||
// the docs better. Or TODO: add a link here in order to help new users.
|
||||
if err := h.addRoute(r); err != nil {
|
||||
// node errors:
|
||||
rp.Add("%v -> %s", err, r.String())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
golog.Debugf(r.Trace())
|
||||
golog.Debugf(r.Trace()) // keep log different parameter types in the same path as different routes.
|
||||
}
|
||||
|
||||
return rp.Return()
|
||||
}
|
||||
|
||||
func bindMultiParamTypesHandler(top *Route, r *Route) {
|
||||
r.BuildHandlers()
|
||||
|
||||
h := r.Handlers[1:] // remove the macro evaluator handler as we manually check below.
|
||||
f := macroHandler.MakeFilter(r.tmpl)
|
||||
if f == nil {
|
||||
return // should never happen, previous checks made to set the top link.
|
||||
}
|
||||
|
||||
decisionHandler := func(ctx context.Context) {
|
||||
currentRouteName := ctx.RouteName()
|
||||
if f(ctx) {
|
||||
ctx.SetCurrentRouteName(r.Name)
|
||||
ctx.HandlerIndex(0)
|
||||
ctx.Do(h)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetCurrentRouteName(currentRouteName)
|
||||
ctx.StatusCode(http.StatusOK)
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
r.topLink.beginHandlers = append(context.Handlers{decisionHandler}, r.topLink.beginHandlers...)
|
||||
}
|
||||
|
||||
func (h *routerHandler) HandleRequest(ctx context.Context) {
|
||||
method := ctx.Method()
|
||||
path := ctx.Path()
|
||||
|
||||
@@ -40,6 +40,8 @@ type Route struct {
|
||||
// route, manually or automatic by the framework,
|
||||
// get the route by `Application#GetRouteByPath(staticSite.RequestPath)`.
|
||||
StaticSites []context.StaticSite `json:"staticSites"`
|
||||
|
||||
topLink *Route
|
||||
}
|
||||
|
||||
// NewRoute returns a new route based on its method,
|
||||
|
||||
4
doc.go
4
doc.go
@@ -38,13 +38,13 @@ Source code and other details for the project are available at GitHub:
|
||||
|
||||
Current Version
|
||||
|
||||
11.2.2
|
||||
11.2.3
|
||||
|
||||
Installation
|
||||
|
||||
The only requirement is the Go Programming Language, at least version 1.12.
|
||||
|
||||
$ go get github.com/kataras/iris@v11.2.2
|
||||
$ go get github.com/kataras/iris@master
|
||||
|
||||
Wiki:
|
||||
|
||||
|
||||
2
iris.go
2
iris.go
@@ -37,7 +37,7 @@ import (
|
||||
|
||||
var (
|
||||
// Version is the current version number of the Iris Web Framework.
|
||||
Version = "11.2.2"
|
||||
Version = "11.2.3"
|
||||
)
|
||||
|
||||
// HTTP status codes as registered with IANA.
|
||||
|
||||
@@ -34,23 +34,54 @@ func CanMakeHandler(tmpl macro.Template) (needsMacroHandler bool) {
|
||||
// If the template does not contain any dynamic attributes and a special handler is NOT required
|
||||
// then it returns a nil handler.
|
||||
func MakeHandler(tmpl macro.Template) context.Handler {
|
||||
filter := MakeFilter(tmpl)
|
||||
|
||||
return func(ctx context.Context) {
|
||||
if !filter(ctx) {
|
||||
ctx.StopExecution()
|
||||
return
|
||||
}
|
||||
|
||||
// if all passed, just continue.
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// MakeFilter returns a Filter which reports whether a specific macro template
|
||||
// and its parameters pass the serve-time validation.
|
||||
func MakeFilter(tmpl macro.Template) context.Filter {
|
||||
if !CanMakeHandler(tmpl) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return func(ctx context.Context) {
|
||||
return func(ctx context.Context) bool {
|
||||
for _, p := range tmpl.Params {
|
||||
if !p.CanEval() {
|
||||
continue // allow.
|
||||
}
|
||||
|
||||
if !p.Eval(ctx.Params().Get(p.Name), &ctx.Params().Store) {
|
||||
// 07-29-2019
|
||||
// changed to retrieve by param index in order to support
|
||||
// different parameter names for routes with
|
||||
// different param types (and probably different param names i.e {name:string}, {id:uint64})
|
||||
// in the exact same path pattern.
|
||||
//
|
||||
// Same parameter names are not allowed, different param types in the same path
|
||||
// should have different name e.g. {name} {id:uint64};
|
||||
// something like {name} and {name:uint64}
|
||||
// is bad API design and we do NOT allow it by-design.
|
||||
entry, found := ctx.Params().Store.GetEntryAt(p.Index)
|
||||
if !found {
|
||||
// should never happen.
|
||||
return false
|
||||
}
|
||||
|
||||
if !p.Eval(entry.String(), &ctx.Params().Store) {
|
||||
ctx.StatusCode(p.ErrCode)
|
||||
ctx.StopExecution()
|
||||
return
|
||||
return false
|
||||
}
|
||||
}
|
||||
// if all passed, just continue.
|
||||
ctx.Next()
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ type Template struct {
|
||||
Params []TemplateParam `json:"params"`
|
||||
}
|
||||
|
||||
// IsTrailing reports whether this Template is a traling one.
|
||||
func (t *Template) IsTrailing() bool {
|
||||
return len(t.Params) > 0 && ast.IsTrailing(t.Params[len(t.Params)-1].Type)
|
||||
}
|
||||
|
||||
// TemplateParam is the parsed macro parameter's template
|
||||
// they are being used to describe the param's syntax result.
|
||||
type TemplateParam struct {
|
||||
|
||||
Reference in New Issue
Block a user