diff --git a/HISTORY.md b/HISTORY.md
index 95eed0fa6..3b7bc94ef 100644
--- a/HISTORY.md
+++ b/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
diff --git a/_examples/miscellaneous/pprof/main.go b/_examples/miscellaneous/pprof/main.go
index 2b2a79e94..80cb9429e 100644
--- a/_examples/miscellaneous/pprof/main.go
+++ b/_examples/miscellaneous/pprof/main.go
@@ -13,7 +13,9 @@ func main() {
ctx.HTML("
Please click here")
})
- 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"))
}
diff --git a/_examples/routing/basic/main.go b/_examples/routing/basic/main.go
index d03ea46c0..8a6c29f1a 100644
--- a/_examples/routing/basic/main.go
+++ b/_examples/routing/basic/main.go
@@ -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
diff --git a/core/router/api_builder.go b/core/router/api_builder.go
index 35005f873..b5aee7a72 100644
--- a/core/router/api_builder.go
+++ b/core/router/api_builder.go
@@ -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)
}
diff --git a/core/router/handler.go b/core/router/handler.go
index 4091e2415..894fefd0f 100644
--- a/core/router/handler.go
+++ b/core/router/handler.go
@@ -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()
diff --git a/core/router/route.go b/core/router/route.go
index a7f53a09e..9746e4231 100644
--- a/core/router/route.go
+++ b/core/router/route.go
@@ -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,
diff --git a/doc.go b/doc.go
index fca949ed2..f9593e802 100644
--- a/doc.go
+++ b/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:
diff --git a/iris.go b/iris.go
index a5aae2296..8cb0f36c4 100644
--- a/iris.go
+++ b/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.
diff --git a/macro/handler/handler.go b/macro/handler/handler.go
index ea36c7d56..1cf744500 100644
--- a/macro/handler/handler.go
+++ b/macro/handler/handler.go
@@ -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
}
}
diff --git a/macro/template.go b/macro/template.go
index 8e766cb5d..fcdb0b096 100644
--- a/macro/template.go
+++ b/macro/template.go
@@ -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 {