New 'Application.UseRouter(...Handler)'. Read HISTORY.md

This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-08-09 22:28:44 +03:00
parent 24de7bf7fb
commit da029d6f37
No known key found for this signature in database
GPG Key ID: 5DBE766BD26A54E7
3 changed files with 102 additions and 35 deletions

View File

@ -359,6 +359,8 @@ Response:
Other Improvements:
- `Application.UseRouter(...Handler)` - to register handlers before the main router, useful on handlers that should control whether the router itself should ran or not. Independently of the incoming request's method and path values. These handlers will be executed ALWAYS against ALL incoming requests. Example of use-case: CORS.
- `*versioning.Group` type is a full `Party` now.
- `Party.UseOnce` - either inserts a middleware, or on the basis of the middleware already existing, replace that existing middleware instead.

View File

@ -18,7 +18,8 @@ import (
// User can refresh the router with `RefreshRouter` whenever a route's field is changed by him.
type Router struct {
mu sync.Mutex // for Downgrade, WrapRouter & BuildRouter,
// not indeed but we don't to risk its usage by third-parties.
preHandlers context.Handlers // run before requestHandler, as middleware, same way context's handlers run, see `UseRouter`.
requestHandler RequestHandler // build-accessible, can be changed to define a custom router or proxy, used on RefreshRouter too.
mainHandler http.HandlerFunc // init-accessible
wrapperFunc WrapperFunc
@ -86,6 +87,25 @@ func (router *Router) FindClosestPaths(subdomain, searchPath string, n int) []st
return list
}
// UseRouter registers one or more handlers that are fired
// before the main router's request handler.
//
// Use this method to register handlers, that can ran
// independently of the incoming request's method and path values,
// that they will be executed ALWAYS against ALL incoming requests.
// Example of use-case: CORS.
//
// Note that because these are executed before the router itself
// the Context should not have access to the `GetCurrentRoute`
// as it is not decided yet which route is responsible to handle the incoming request.
// It's one level higher than the `WrapRouter`.
// The context SHOULD call its `Next` method in order to proceed to
// the next handler in the chain or the main request handler one.
// ExecutionRules are NOT applied here.
func (router *Router) UseRouter(handlers ...context.Handler) {
router.preHandlers = append(router.preHandlers, handlers...)
}
// BuildRouter builds the router based on
// the context factory (explicit pool in this case),
// the request handler which manages how the main handler will multiplexes the routes
@ -130,13 +150,28 @@ func (router *Router) BuildRouter(cPool *context.Pool, requestHandler RequestHan
}
// the important
if len(router.preHandlers) > 0 {
handlers := append(router.preHandlers, func(ctx *context.Context) {
// set the handler index back to 0 so the route's handlers can be executed as exepcted.
ctx.HandlerIndex(0)
// execute the main request handler, this will fire the found route's handlers
// or if error the error code's associated handler.
router.requestHandler.HandleRequest(ctx)
})
router.mainHandler = func(w http.ResponseWriter, r *http.Request) {
ctx := cPool.Acquire(w, r)
// execute the final handlers chain.
ctx.Do(handlers)
cPool.Release(ctx)
}
} else {
router.mainHandler = func(w http.ResponseWriter, r *http.Request) {
ctx := cPool.Acquire(w, r)
// Note: we can't get all r.Context().Value key-value pairs
// and save them to ctx.values.
router.requestHandler.HandleRequest(ctx)
cPool.Release(ctx)
}
}
if router.wrapperFunc != nil { // if wrapper used then attach that as the router service
router.mainHandler = newWrapper(router.wrapperFunc, router.mainHandler).ServeHTTP

View File

@ -7,11 +7,10 @@
package router_test
import (
"fmt"
"testing"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/httptest"
)
@ -19,48 +18,43 @@ import (
// with a different order but the route's final
// response should be the same at all cases.
var (
mainResponse = "main"
mainHandler = func(ctx *context.Context) {
ctx.WriteString(mainResponse)
writeHandler = func(s string) iris.Handler {
return func(ctx iris.Context) {
ctx.WriteString(s)
ctx.Next()
}
}
mainResponse = "main"
mainHandler = writeHandler(mainResponse)
firstUseResponse = "use1"
firstUseHandler = func(ctx *context.Context) {
ctx.WriteString(firstUseResponse)
ctx.Next()
}
firstUseHandler = writeHandler(firstUseResponse)
secondUseResponse = "use2"
secondUseHandler = func(ctx *context.Context) {
ctx.WriteString(secondUseResponse)
ctx.Next()
}
secondUseHandler = writeHandler(secondUseResponse)
firstUseRouterResponse = "userouter1"
firstUseRouterHandler = writeHandler(firstUseRouterResponse)
secondUseRouterResponse = "userouter2"
secondUseRouterHandler = writeHandler(secondUseRouterResponse)
firstUseGlobalResponse = "useglobal1"
firstUseGlobalHandler = func(ctx *context.Context) {
ctx.WriteString(firstUseGlobalResponse)
ctx.Next()
}
firstUseGlobalHandler = writeHandler(firstUseGlobalResponse)
secondUseGlobalResponse = "useglobal2"
secondUseGlobalHandler = func(ctx *context.Context) {
ctx.WriteString(secondUseGlobalResponse)
ctx.Next()
}
secondUseGlobalHandler = writeHandler(secondUseGlobalResponse)
firstDoneResponse = "done1"
firstDoneHandler = func(ctx *context.Context) {
ctx.WriteString(firstDoneResponse)
ctx.Next()
}
firstDoneHandler = writeHandler(firstDoneResponse)
secondDoneResponse = "done2"
secondDoneHandler = func(ctx *context.Context) {
secondDoneHandler = func(ctx iris.Context) {
ctx.WriteString(secondDoneResponse)
}
finalResponse = firstUseGlobalResponse + secondUseGlobalResponse +
finalResponse = firstUseRouterResponse + secondUseRouterResponse + firstUseGlobalResponse + secondUseGlobalResponse +
firstUseResponse + secondUseResponse + mainResponse + firstDoneResponse + secondDoneResponse
testResponse = func(t *testing.T, app *iris.Application, path string) {
@ -73,6 +67,9 @@ var (
func TestMiddlewareByRouteDef(t *testing.T) {
app := iris.New()
app.UseRouter(firstUseRouterHandler)
app.UseRouter(secondUseRouterHandler)
app.Get("/mypath", firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler,
mainHandler, firstDoneHandler, secondDoneHandler)
@ -81,6 +78,7 @@ func TestMiddlewareByRouteDef(t *testing.T) {
func TestMiddlewareByUseAndDoneDef(t *testing.T) {
app := iris.New()
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
app.Use(firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler)
app.Done(firstDoneHandler, secondDoneHandler)
@ -91,12 +89,14 @@ func TestMiddlewareByUseAndDoneDef(t *testing.T) {
func TestMiddlewareByUseUseGlobalAndDoneDef(t *testing.T) {
app := iris.New()
app.Use(firstUseHandler, secondUseHandler)
// if failed then UseGlobal didnt' registered these handlers even before the
// existing middleware.
app.UseGlobal(firstUseGlobalHandler, secondUseGlobalHandler)
app.Done(firstDoneHandler, secondDoneHandler)
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
app.Get("/mypath", mainHandler)
testResponse(t, app, "/mypath")
@ -104,6 +104,7 @@ func TestMiddlewareByUseUseGlobalAndDoneDef(t *testing.T) {
func TestMiddlewareByUseDoneAndUseGlobalDef(t *testing.T) {
app := iris.New()
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
app.Use(firstUseHandler, secondUseHandler)
app.Done(firstDoneHandler, secondDoneHandler)
@ -123,6 +124,8 @@ func TestMiddlewareByUseDoneAndUseGlobalDef(t *testing.T) {
func TestMiddlewareByUseGlobalUseAndDoneGlobalDef(t *testing.T) {
app := iris.New()
app.UseRouter(firstUseRouterHandler)
app.UseRouter(secondUseRouterHandler)
app.UseGlobal(firstUseGlobalHandler)
app.UseGlobal(secondUseGlobalHandler)
@ -137,6 +140,7 @@ func TestMiddlewareByUseGlobalUseAndDoneGlobalDef(t *testing.T) {
func TestMiddlewareByDoneUseAndUseGlobalDef(t *testing.T) {
app := iris.New()
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
app.Done(firstDoneHandler, secondDoneHandler)
app.Use(firstUseHandler, secondUseHandler)
@ -148,3 +152,29 @@ func TestMiddlewareByDoneUseAndUseGlobalDef(t *testing.T) {
testResponse(t, app, "/mypath")
}
func TestUseRouterStopExecution(t *testing.T) {
app := iris.New()
app.UseRouter(func(ctx iris.Context) {
ctx.WriteString("stop")
// no ctx.Next, so the router has not even the chance to work.
})
app.Get("/", writeHandler("index"))
e := httptest.New(t, app)
e.GET("/").Expect().Status(iris.StatusOK).Body().Equal("stop")
app = iris.New()
app.OnErrorCode(iris.StatusForbidden, func(ctx iris.Context) {
ctx.Writef("err: %v", ctx.GetErr())
})
app.UseRouter(func(ctx iris.Context) {
ctx.StopWithPlainError(iris.StatusForbidden, fmt.Errorf("custom error"))
// stopped but not data written yet, the error code handler
// should be responsible of it (use StopWithError to write and close).
})
app.Get("/", writeHandler("index"))
e = httptest.New(t, app)
e.GET("/").Expect().Status(iris.StatusForbidden).Body().Equal("err: custom error")
}