mirror of
https://github.com/kataras/iris.git
synced 2026-01-08 12:31:58 +00:00
implement #1536 with (SetRegisterRule(iris.RouteOverlap))
Former-commit-id: 2b5523ff3e2aab60dd83faa3c520b16a34916fbe
This commit is contained in:
@@ -18,21 +18,19 @@ func FromStd(handler interface{}) context.Handler {
|
||||
case context.Handler:
|
||||
{
|
||||
//
|
||||
// it's already a iris handler
|
||||
// it's already an Iris Handler
|
||||
//
|
||||
return h
|
||||
}
|
||||
|
||||
case http.Handler:
|
||||
//
|
||||
// handlerFunc.ServeHTTP(w,r)
|
||||
//
|
||||
{
|
||||
//
|
||||
// handlerFunc.ServeHTTP(w,r)
|
||||
//
|
||||
return func(ctx context.Context) {
|
||||
h.ServeHTTP(ctx.ResponseWriter(), ctx.Request())
|
||||
}
|
||||
}
|
||||
|
||||
case func(http.ResponseWriter, *http.Request):
|
||||
{
|
||||
//
|
||||
@@ -40,7 +38,6 @@ func FromStd(handler interface{}) context.Handler {
|
||||
//
|
||||
return FromStd(http.HandlerFunc(h))
|
||||
}
|
||||
|
||||
case func(http.ResponseWriter, *http.Request, http.HandlerFunc):
|
||||
{
|
||||
//
|
||||
@@ -48,7 +45,6 @@ func FromStd(handler interface{}) context.Handler {
|
||||
//
|
||||
return FromStdWithNext(h)
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
//
|
||||
@@ -60,9 +56,8 @@ func FromStd(handler interface{}) context.Handler {
|
||||
- func(w http.ResponseWriter, r *http.Request)
|
||||
- func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
|
||||
---------------------------------------------------------------------
|
||||
It seems to be a %T points to: %v`, handler, handler))
|
||||
It seems to be a %T points to: %v`, handler, handler))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,10 +65,10 @@ func FromStd(handler interface{}) context.Handler {
|
||||
// compatible context.Handler wrapper.
|
||||
func FromStdWithNext(h func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)) context.Handler {
|
||||
return func(ctx context.Context) {
|
||||
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
next := func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx.ResetRequest(r)
|
||||
ctx.Next()
|
||||
})
|
||||
}
|
||||
|
||||
h(ctx.ResponseWriter(), ctx.Request(), next)
|
||||
}
|
||||
|
||||
@@ -91,6 +91,9 @@ func (repo *repository) register(route *Route, rule RouteRegisterRule) (*Route,
|
||||
return r, nil
|
||||
} else if rule == RouteError {
|
||||
return nil, fmt.Errorf("new route: %s conflicts with an already registered one: %s route", route.String(), r.String())
|
||||
} else if rule == RouteOverlap {
|
||||
overlapRoute(r, route)
|
||||
return route, nil
|
||||
} else {
|
||||
// replace existing with the latest one, the default behavior.
|
||||
repo.routes = append(repo.routes[:i], repo.routes[i+1:]...)
|
||||
@@ -113,6 +116,38 @@ func (repo *repository) register(route *Route, rule RouteRegisterRule) (*Route,
|
||||
return route, nil
|
||||
}
|
||||
|
||||
var defaultOverlapFilter = func(ctx context.Context) bool {
|
||||
if ctx.IsStopped() {
|
||||
// It's stopped and the response can be overriden by a new handler.
|
||||
rs, ok := ctx.ResponseWriter().(context.ResponseWriterReseter)
|
||||
return ok && rs.Reset()
|
||||
}
|
||||
|
||||
// It's not stopped, all OK no need to execute the alternative route.
|
||||
return false
|
||||
}
|
||||
|
||||
func overlapRoute(r *Route, next *Route) {
|
||||
next.BuildHandlers()
|
||||
nextHandlers := next.Handlers[0:]
|
||||
|
||||
decisionHandler := func(ctx context.Context) {
|
||||
ctx.Next()
|
||||
|
||||
if !defaultOverlapFilter(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetCurrentRoute(next.ReadOnly)
|
||||
ctx.HandlerIndex(0)
|
||||
ctx.Do(nextHandlers)
|
||||
}
|
||||
|
||||
// NOTE(@kataras): Any UseGlobal call will prepend to this, if they are
|
||||
// in the same Party then it's expected, otherwise not.
|
||||
r.beginHandlers = append(context.Handlers{decisionHandler}, r.beginHandlers...)
|
||||
}
|
||||
|
||||
// APIBuilder the visible API for constructing the router
|
||||
// and child routers.
|
||||
type APIBuilder struct {
|
||||
@@ -261,10 +296,17 @@ const (
|
||||
// RouteError log when a route already exists, shown after the `Build` state,
|
||||
// server never starts.
|
||||
RouteError
|
||||
// RouteOverlap will overlap the new route to the previous one.
|
||||
// If the route stopped and its response can be reset then the new route will be execute.
|
||||
RouteOverlap
|
||||
)
|
||||
|
||||
// SetRegisterRule sets a `RouteRegisterRule` for this Party and its children.
|
||||
// Available values are: RouteOverride (the default one), RouteSkip and RouteError.
|
||||
// Available values are:
|
||||
// * RouteOverride (the default one)
|
||||
// * RouteSkip
|
||||
// * RouteError
|
||||
// * RouteOverlap.
|
||||
func (api *APIBuilder) SetRegisterRule(rule RouteRegisterRule) Party {
|
||||
api.routeRegisterRule = rule
|
||||
return api
|
||||
|
||||
@@ -116,7 +116,11 @@ type Party interface {
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/mvc/middleware/without-ctx-next
|
||||
SetExecutionRules(executionRules ExecutionRules) Party
|
||||
// SetRegisterRule sets a `RouteRegisterRule` for this Party and its children.
|
||||
// Available values are: RouteOverride (the default one), RouteSkip and RouteError.
|
||||
// Available values are:
|
||||
// * RouteOverride (the default one)
|
||||
// * RouteSkip
|
||||
// * RouteError
|
||||
// * RouteOverlap.
|
||||
SetRegisterRule(rule RouteRegisterRule) Party
|
||||
|
||||
// Handle registers a route to the server's router.
|
||||
|
||||
@@ -66,6 +66,9 @@ type Route struct {
|
||||
|
||||
// ReadOnly is the read-only structure of the Route.
|
||||
ReadOnly context.RouteReadOnly
|
||||
|
||||
// OnBuild runs right before BuildHandlers.
|
||||
OnBuild func(r *Route)
|
||||
}
|
||||
|
||||
// NewRoute returns a new route based on its method,
|
||||
@@ -186,6 +189,10 @@ func (r *Route) RestoreStatus() bool {
|
||||
// at the `Application#Build` state. Do not call it manually, unless
|
||||
// you were defined your own request mux handler.
|
||||
func (r *Route) BuildHandlers() {
|
||||
if r.OnBuild != nil {
|
||||
r.OnBuild(r)
|
||||
}
|
||||
|
||||
if len(r.beginHandlers) > 0 {
|
||||
r.Handlers = append(r.beginHandlers, r.Handlers...)
|
||||
r.beginHandlers = r.beginHandlers[0:0]
|
||||
|
||||
@@ -58,3 +58,54 @@ func testRegisterRule(e *httptest.Expect, expectedGetBody string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterRuleOverlap(t *testing.T) {
|
||||
app := iris.New()
|
||||
// TODO(@kataras) the overlapping does not work per-party yet,
|
||||
// it just checks compares from the total app's routes (which is the best possible action to do
|
||||
// because MVC applications can be separated into different parties too?).
|
||||
usersRouter := app.Party("/users")
|
||||
usersRouter.SetRegisterRule(iris.RouteOverlap)
|
||||
|
||||
// second handler will be executed, status will be reset-ed as well,
|
||||
// stop without data written.
|
||||
usersRouter.Get("/", func(ctx iris.Context) {
|
||||
ctx.StopWithStatus(iris.StatusUnauthorized)
|
||||
})
|
||||
usersRouter.Get("/", func(ctx iris.Context) {
|
||||
ctx.WriteString("data")
|
||||
})
|
||||
|
||||
// first handler will be executed, no stop called.
|
||||
usersRouter.Get("/p1", func(ctx iris.Context) {
|
||||
ctx.StatusCode(iris.StatusUnauthorized)
|
||||
})
|
||||
usersRouter.Get("/p1", func(ctx iris.Context) {
|
||||
ctx.WriteString("not written")
|
||||
})
|
||||
|
||||
// first handler will be executed, stop but with data sent on default writer
|
||||
// (body sent cannot be reset-ed here).
|
||||
usersRouter.Get("/p2", func(ctx iris.Context) {
|
||||
ctx.StopWithText(iris.StatusUnauthorized, "no access")
|
||||
})
|
||||
usersRouter.Get("/p2", func(ctx iris.Context) {
|
||||
ctx.WriteString("not written")
|
||||
})
|
||||
|
||||
// second will be executed, response can be reset-ed on recording.
|
||||
usersRouter.Get("/p3", func(ctx iris.Context) {
|
||||
ctx.Record()
|
||||
ctx.StopWithText(iris.StatusUnauthorized, "no access")
|
||||
})
|
||||
usersRouter.Get("/p3", func(ctx iris.Context) {
|
||||
ctx.WriteString("p3 data")
|
||||
})
|
||||
|
||||
e := httptest.New(t, app)
|
||||
|
||||
e.GET("/users").Expect().Status(httptest.StatusOK).Body().Equal("data")
|
||||
e.GET("/users/p1").Expect().Status(httptest.StatusUnauthorized).Body().Equal("Unauthorized")
|
||||
e.GET("/users/p2").Expect().Status(httptest.StatusUnauthorized).Body().Equal("no access")
|
||||
e.GET("/users/p3").Expect().Status(httptest.StatusOK).Body().Equal("p3 data")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user