Skip to content

Commit da029d6

Browse files
committed
New 'Application.UseRouter(...Handler)'. Read HISTORY.md
1 parent 24de7bf commit da029d6

File tree

3 files changed

+102
-35
lines changed

3 files changed

+102
-35
lines changed

HISTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,8 @@ Response:
359359

360360
Other Improvements:
361361

362+
- `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.
363+
362364
- `*versioning.Group` type is a full `Party` now.
363365

364366
- `Party.UseOnce` - either inserts a middleware, or on the basis of the middleware already existing, replace that existing middleware instead.

core/router/router.go

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import (
1818
// User can refresh the router with `RefreshRouter` whenever a route's field is changed by him.
1919
type Router struct {
2020
mu sync.Mutex // for Downgrade, WrapRouter & BuildRouter,
21-
// not indeed but we don't to risk its usage by third-parties.
21+
22+
preHandlers context.Handlers // run before requestHandler, as middleware, same way context's handlers run, see `UseRouter`.
2223
requestHandler RequestHandler // build-accessible, can be changed to define a custom router or proxy, used on RefreshRouter too.
2324
mainHandler http.HandlerFunc // init-accessible
2425
wrapperFunc WrapperFunc
@@ -86,6 +87,25 @@ func (router *Router) FindClosestPaths(subdomain, searchPath string, n int) []st
8687
return list
8788
}
8889

90+
// UseRouter registers one or more handlers that are fired
91+
// before the main router's request handler.
92+
//
93+
// Use this method to register handlers, that can ran
94+
// independently of the incoming request's method and path values,
95+
// that they will be executed ALWAYS against ALL incoming requests.
96+
// Example of use-case: CORS.
97+
//
98+
// Note that because these are executed before the router itself
99+
// the Context should not have access to the `GetCurrentRoute`
100+
// as it is not decided yet which route is responsible to handle the incoming request.
101+
// It's one level higher than the `WrapRouter`.
102+
// The context SHOULD call its `Next` method in order to proceed to
103+
// the next handler in the chain or the main request handler one.
104+
// ExecutionRules are NOT applied here.
105+
func (router *Router) UseRouter(handlers ...context.Handler) {
106+
router.preHandlers = append(router.preHandlers, handlers...)
107+
}
108+
89109
// BuildRouter builds the router based on
90110
// the context factory (explicit pool in this case),
91111
// the request handler which manages how the main handler will multiplexes the routes
@@ -130,12 +150,27 @@ func (router *Router) BuildRouter(cPool *context.Pool, requestHandler RequestHan
130150
}
131151

132152
// the important
133-
router.mainHandler = func(w http.ResponseWriter, r *http.Request) {
134-
ctx := cPool.Acquire(w, r)
135-
// Note: we can't get all r.Context().Value key-value pairs
136-
// and save them to ctx.values.
137-
router.requestHandler.HandleRequest(ctx)
138-
cPool.Release(ctx)
153+
if len(router.preHandlers) > 0 {
154+
handlers := append(router.preHandlers, func(ctx *context.Context) {
155+
// set the handler index back to 0 so the route's handlers can be executed as exepcted.
156+
ctx.HandlerIndex(0)
157+
// execute the main request handler, this will fire the found route's handlers
158+
// or if error the error code's associated handler.
159+
router.requestHandler.HandleRequest(ctx)
160+
})
161+
162+
router.mainHandler = func(w http.ResponseWriter, r *http.Request) {
163+
ctx := cPool.Acquire(w, r)
164+
// execute the final handlers chain.
165+
ctx.Do(handlers)
166+
cPool.Release(ctx)
167+
}
168+
} else {
169+
router.mainHandler = func(w http.ResponseWriter, r *http.Request) {
170+
ctx := cPool.Acquire(w, r)
171+
router.requestHandler.HandleRequest(ctx)
172+
cPool.Release(ctx)
173+
}
139174
}
140175

141176
if router.wrapperFunc != nil { // if wrapper used then attach that as the router service

core/router/router_handlers_order_test.go

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,60 +7,54 @@
77
package router_test
88

99
import (
10+
"fmt"
1011
"testing"
1112

1213
"github.com/kataras/iris/v12"
13-
"github.com/kataras/iris/v12/context"
14-
1514
"github.com/kataras/iris/v12/httptest"
1615
)
1716

1817
// test registering of below handlers
1918
// with a different order but the route's final
2019
// response should be the same at all cases.
2120
var (
22-
mainResponse = "main"
23-
mainHandler = func(ctx *context.Context) {
24-
ctx.WriteString(mainResponse)
25-
ctx.Next()
21+
writeHandler = func(s string) iris.Handler {
22+
return func(ctx iris.Context) {
23+
ctx.WriteString(s)
24+
ctx.Next()
25+
}
2626
}
2727

28+
mainResponse = "main"
29+
mainHandler = writeHandler(mainResponse)
30+
2831
firstUseResponse = "use1"
29-
firstUseHandler = func(ctx *context.Context) {
30-
ctx.WriteString(firstUseResponse)
31-
ctx.Next()
32-
}
32+
firstUseHandler = writeHandler(firstUseResponse)
3333

3434
secondUseResponse = "use2"
35-
secondUseHandler = func(ctx *context.Context) {
36-
ctx.WriteString(secondUseResponse)
37-
ctx.Next()
38-
}
35+
secondUseHandler = writeHandler(secondUseResponse)
36+
37+
firstUseRouterResponse = "userouter1"
38+
firstUseRouterHandler = writeHandler(firstUseRouterResponse)
39+
40+
secondUseRouterResponse = "userouter2"
41+
secondUseRouterHandler = writeHandler(secondUseRouterResponse)
3942

4043
firstUseGlobalResponse = "useglobal1"
41-
firstUseGlobalHandler = func(ctx *context.Context) {
42-
ctx.WriteString(firstUseGlobalResponse)
43-
ctx.Next()
44-
}
44+
firstUseGlobalHandler = writeHandler(firstUseGlobalResponse)
4545

4646
secondUseGlobalResponse = "useglobal2"
47-
secondUseGlobalHandler = func(ctx *context.Context) {
48-
ctx.WriteString(secondUseGlobalResponse)
49-
ctx.Next()
50-
}
47+
secondUseGlobalHandler = writeHandler(secondUseGlobalResponse)
5148

5249
firstDoneResponse = "done1"
53-
firstDoneHandler = func(ctx *context.Context) {
54-
ctx.WriteString(firstDoneResponse)
55-
ctx.Next()
56-
}
50+
firstDoneHandler = writeHandler(firstDoneResponse)
5751

5852
secondDoneResponse = "done2"
59-
secondDoneHandler = func(ctx *context.Context) {
53+
secondDoneHandler = func(ctx iris.Context) {
6054
ctx.WriteString(secondDoneResponse)
6155
}
6256

63-
finalResponse = firstUseGlobalResponse + secondUseGlobalResponse +
57+
finalResponse = firstUseRouterResponse + secondUseRouterResponse + firstUseGlobalResponse + secondUseGlobalResponse +
6458
firstUseResponse + secondUseResponse + mainResponse + firstDoneResponse + secondDoneResponse
6559

6660
testResponse = func(t *testing.T, app *iris.Application, path string) {
@@ -73,6 +67,9 @@ var (
7367

7468
func TestMiddlewareByRouteDef(t *testing.T) {
7569
app := iris.New()
70+
app.UseRouter(firstUseRouterHandler)
71+
app.UseRouter(secondUseRouterHandler)
72+
7673
app.Get("/mypath", firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler,
7774
mainHandler, firstDoneHandler, secondDoneHandler)
7875

@@ -81,6 +78,7 @@ func TestMiddlewareByRouteDef(t *testing.T) {
8178

8279
func TestMiddlewareByUseAndDoneDef(t *testing.T) {
8380
app := iris.New()
81+
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
8482
app.Use(firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler)
8583
app.Done(firstDoneHandler, secondDoneHandler)
8684

@@ -91,19 +89,22 @@ func TestMiddlewareByUseAndDoneDef(t *testing.T) {
9189

9290
func TestMiddlewareByUseUseGlobalAndDoneDef(t *testing.T) {
9391
app := iris.New()
92+
9493
app.Use(firstUseHandler, secondUseHandler)
9594
// if failed then UseGlobal didnt' registered these handlers even before the
9695
// existing middleware.
9796
app.UseGlobal(firstUseGlobalHandler, secondUseGlobalHandler)
9897
app.Done(firstDoneHandler, secondDoneHandler)
9998

99+
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
100100
app.Get("/mypath", mainHandler)
101101

102102
testResponse(t, app, "/mypath")
103103
}
104104

105105
func TestMiddlewareByUseDoneAndUseGlobalDef(t *testing.T) {
106106
app := iris.New()
107+
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
107108

108109
app.Use(firstUseHandler, secondUseHandler)
109110
app.Done(firstDoneHandler, secondDoneHandler)
@@ -123,6 +124,8 @@ func TestMiddlewareByUseDoneAndUseGlobalDef(t *testing.T) {
123124

124125
func TestMiddlewareByUseGlobalUseAndDoneGlobalDef(t *testing.T) {
125126
app := iris.New()
127+
app.UseRouter(firstUseRouterHandler)
128+
app.UseRouter(secondUseRouterHandler)
126129

127130
app.UseGlobal(firstUseGlobalHandler)
128131
app.UseGlobal(secondUseGlobalHandler)
@@ -137,6 +140,7 @@ func TestMiddlewareByUseGlobalUseAndDoneGlobalDef(t *testing.T) {
137140

138141
func TestMiddlewareByDoneUseAndUseGlobalDef(t *testing.T) {
139142
app := iris.New()
143+
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
140144
app.Done(firstDoneHandler, secondDoneHandler)
141145

142146
app.Use(firstUseHandler, secondUseHandler)
@@ -148,3 +152,29 @@ func TestMiddlewareByDoneUseAndUseGlobalDef(t *testing.T) {
148152

149153
testResponse(t, app, "/mypath")
150154
}
155+
156+
func TestUseRouterStopExecution(t *testing.T) {
157+
app := iris.New()
158+
app.UseRouter(func(ctx iris.Context) {
159+
ctx.WriteString("stop")
160+
// no ctx.Next, so the router has not even the chance to work.
161+
})
162+
app.Get("/", writeHandler("index"))
163+
164+
e := httptest.New(t, app)
165+
e.GET("/").Expect().Status(iris.StatusOK).Body().Equal("stop")
166+
167+
app = iris.New()
168+
app.OnErrorCode(iris.StatusForbidden, func(ctx iris.Context) {
169+
ctx.Writef("err: %v", ctx.GetErr())
170+
})
171+
app.UseRouter(func(ctx iris.Context) {
172+
ctx.StopWithPlainError(iris.StatusForbidden, fmt.Errorf("custom error"))
173+
// stopped but not data written yet, the error code handler
174+
// should be responsible of it (use StopWithError to write and close).
175+
})
176+
app.Get("/", writeHandler("index"))
177+
178+
e = httptest.New(t, app)
179+
e.GET("/").Expect().Status(iris.StatusForbidden).Body().Equal("err: custom error")
180+
}

0 commit comments

Comments
 (0)