From 66209cae4f8e3df669f9be0489f895a35819b9e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Meyer?= Date: Wed, 21 Feb 2018 08:18:53 +0300 Subject: [PATCH] Save Former-commit-id: 592e9cddf3511fc08e87f19ad39fdaac479b453f --- _examples/routing/route-state/main.go | 2 +- core/router/api_builder.go | 19 ++++++++++-- core/router/fallback_stack.go | 44 +++++++++++++++++++++++++++ core/router/handler.go | 9 ++++-- core/router/party.go | 7 +++++ mvc/mvc.go | 7 +++++ 6 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 core/router/fallback_stack.go diff --git a/_examples/routing/route-state/main.go b/_examples/routing/route-state/main.go index f85426f8..ec4f03db 100644 --- a/_examples/routing/route-state/main.go +++ b/_examples/routing/route-state/main.go @@ -8,7 +8,7 @@ func main() { app := iris.New() none := app.None("/invisible/{username}", func(ctx iris.Context) { - ctx.Writef("Hello %s with method: %s", ctx.Values().GetString("username"), ctx.Method()) + ctx.Writef("Hello %s with method: %s", ctx.Params().Get("username"), ctx.Method()) if from := ctx.Values().GetString("from"); from != "" { ctx.Writef("\nI see that you're coming from %s", from) diff --git a/core/router/api_builder.go b/core/router/api_builder.go index e9afefba..e0cfc4d7 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -90,6 +90,8 @@ type APIBuilder struct { doneHandlers context.Handlers // global done handlers, order doesn't matter doneGlobalHandlers context.Handlers + // fallback stack, LIFO order + fallbackStack *FallbackStack // the per-party relativePath string } @@ -106,6 +108,7 @@ func NewAPIBuilder() *APIBuilder { reporter: errors.NewReporter(), relativePath: "/", routes: new(repository), + fallbackStack: NewFallbackStack(), } return api @@ -268,9 +271,10 @@ func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) P doneGlobalHandlers: api.doneGlobalHandlers, reporter: api.reporter, // per-party/children - middleware: middleware, - doneHandlers: api.doneHandlers, - relativePath: fullpath, + middleware: middleware, + doneHandlers: api.doneHandlers, + fallbackStack: api.fallbackStack, + relativePath: fullpath, } } @@ -422,6 +426,15 @@ func (api *APIBuilder) DoneGlobal(handlers ...context.Handler) { api.doneGlobalHandlers = append(api.doneGlobalHandlers, handlers...) } +// Fallback appends Handler(s) to the current Party's fallback stack. +// Handler(s) is(are) called from Fallback stack when no route found and before sending NotFound status. +// Therefore Handler(s) in Fallback stack could send another thing than NotFound status, +// if `Context.Next()` method is not called. +// Done & DoneGlobal Handlers are not called. +func (api *APIBuilder) Fallback(middleware ...context.Handler) { + api.fallbackStack.add(middleware) +} + // Reset removes all the begin and done handlers that may derived from the parent party via `Use` & `Done`, // note that the `Reset` will not reset the handlers that are registered via `UseGlobal` & `DoneGlobal`. // diff --git a/core/router/fallback_stack.go b/core/router/fallback_stack.go new file mode 100644 index 00000000..499e0896 --- /dev/null +++ b/core/router/fallback_stack.go @@ -0,0 +1,44 @@ +package router + +import ( + "net/http" + "sync" + + "github.com/kataras/iris/context" +) + +type FallbackStack struct { + handlers context.Handlers + m sync.Mutex +} + +func (stk *FallbackStack) add(h context.Handlers) { + stk.m.Lock() + defer stk.m.Unlock() + + stk.handlers = append(stk.handlers, h...) + + copy(stk.handlers[len(h):], stk.handlers) + copy(stk.handlers, h) +} + +func (stk *FallbackStack) list() context.Handlers { + res := make(context.Handlers, len(stk.handlers)) + + stk.m.Lock() + defer stk.m.Unlock() + + copy(res, stk.handlers) + + return res +} + +func NewFallbackStack() *FallbackStack { + return &FallbackStack{ + handlers: context.Handlers{ + func(ctx context.Context) { + ctx.StatusCode(http.StatusNotFound) + }, + }, + } +} diff --git a/core/router/handler.go b/core/router/handler.go index bb00eb3a..bd8aed9a 100644 --- a/core/router/handler.go +++ b/core/router/handler.go @@ -33,8 +33,9 @@ type tree struct { } type routerHandler struct { - trees []*tree - hosts bool // true if at least one route contains a Subdomain. + trees []*tree + hosts bool // true if at least one route contains a Subdomain. + fallbackStack *FallbackStack } var _ RequestHandler = &routerHandler{} @@ -82,6 +83,8 @@ func NewDefaultHandler() RequestHandler { type RoutesProvider interface { // api builder GetRoutes() []*Route GetRoute(routeName string) *Route + + FallBackStack() *FallbackStack } func (h *routerHandler) Build(provider RoutesProvider) error { @@ -242,5 +245,5 @@ func (h *routerHandler) HandleRequest(ctx context.Context) { } } - ctx.StatusCode(http.StatusNotFound) + ctx.Do(h.fallbackStack.list()) } diff --git a/core/router/party.go b/core/router/party.go index 6f0e814e..a70c6030 100644 --- a/core/router/party.go +++ b/core/router/party.go @@ -60,6 +60,13 @@ type Party interface { // If the current Party is the root, then it registers the middleware to all child Parties' routes too. Use(middleware ...context.Handler) + // Fallback appends Handler(s) to the current Party's fallback stack. + // Handler(s) is(are) called from Fallback stack when no route found and before sending NotFound status. + // Therefore Handler(s) in Fallback stack could send another thing than NotFound status, + // if `Context.Next()` method is not called. + // Done Handler(s) is(are) not called. + Fallback(middleware ...context.Handler) + // Done appends to the very end, Handler(s) to the current Party's routes and child routes. // The difference from .Use is that this/or these Handler(s) are being always running last. Done(handlers ...context.Handler) diff --git a/mvc/mvc.go b/mvc/mvc.go index f11e02d4..35b21cba 100644 --- a/mvc/mvc.go +++ b/mvc/mvc.go @@ -176,6 +176,13 @@ func (app *Application) Handle(controller interface{}) *Application { return app } +// Fallback is an alias to `app.Router.Fallback(handlers...)` +// +// See `core/router#Party.Fallback` +func (app *Application) Fallback(handlers ...context.Handler) { + app.Router.Fallback(handlers...) +} + // Clone returns a new mvc Application which has the dependencies // of the current mvc Mpplication's dependencies. //