Former-commit-id: a42c54708297e6fed78d6fdc2b7cd5ee91c9574b
This commit is contained in:
kataras 2017-07-23 12:10:51 +03:00
parent 11f2f296ba
commit 726d89fd1b
10 changed files with 257 additions and 37 deletions

View File

@ -17,6 +17,10 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`.
# Sa, 23 July 2017 | v8.0.7
Fix [It's true that with UseGlobal the "/path1.txt" route call the middleware but cause the prepend, the order is inversed](https://github.com/kataras/iris/issues/683#issuecomment-317229068)
# Sa, 22 July 2017 | v8.0.5 & v8.0.6
No API Changes.

View File

@ -17,7 +17,7 @@ Iris is a fast, simple and efficient micro web framework for Go. It provides a b
### 📑 Table of contents
* [Installation](#-installation)
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#sa-22-july-2017--v805--v806)
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#sa-23-july-2017--v807)
* [Learn](#-learn)
* [HTTP Listening](_examples/#http-listening)
* [Configuration](_examples/#configuration)
@ -339,7 +339,7 @@ Thank You for your trust!
### 📌 Version
Current: **8.0.6**
Current: **8.0.7**
Each new release is pushed to the master. It stays there until the next version. When a next version is released then the previous version goes to its own branch with `gopkg.in` as its import path (and its own vendor folder), in order to keep it working "for-ever".

View File

@ -47,7 +47,7 @@ var urls = []resource{
func TestSPA(t *testing.T) {
app := newApp()
e := httptest.New(t, app, httptest.Debug(true))
e := httptest.New(t, app, httptest.Debug(false))
for _, u := range urls {
url := u.String()

View File

@ -76,12 +76,20 @@ type APIBuilder struct {
// to the end-user.
reporter *errors.Reporter
// the per-party middleware
// the per-party handlers, order
// of handlers registration matters.
middleware context.Handlers
// the per-party routes (useful only for done middleware)
// the global middleware handlers, order of call doesn't matters, order
// of handlers registration matters. We need a secondary field for this
// because `UseGlobal` registers handlers that should be executed
// even before the `middleware` handlers, and in the same time keep the order
// of handlers registration, so the same type of handlers are being called in order.
beginGlobalHandlers context.Handlers
// the per-party routes registry (useful for `Done` and `UseGlobal` only)
apiRoutes []*Route
// the per-party done middleware
doneHandlers context.Handlers
// the per-party done handlers, order
// of handlers registration matters.
doneGlobalHandlers context.Handlers
// the per-party
relativePath string
}
@ -138,13 +146,16 @@ func (rb *APIBuilder) Handle(method string, registeredPath string, handlers ...c
fullpath := rb.relativePath + registeredPath // for now, keep the last "/" if any, "/xyz/"
routeHandlers := joinHandlers(rb.middleware, handlers)
// global begin handlers -> middleware that are registered before route registration
// -> handlers that are passed to this Handle function.
routeHandlers := joinHandlers(append(rb.beginGlobalHandlers, rb.middleware...), handlers)
// -> done handlers after all
if len(rb.doneGlobalHandlers) > 0 {
routeHandlers = append(routeHandlers, rb.doneGlobalHandlers...) // register the done middleware, if any
}
// here we separate the subdomain and relative path
subdomain, path := splitSubdomainAndPath(fullpath)
if len(rb.doneHandlers) > 0 {
routeHandlers = append(routeHandlers, rb.doneHandlers...) // register the done middleware, if any
}
r, err := NewRoute(method, subdomain, path, routeHandlers, rb.macros)
if err != nil { // template path parser errors:
@ -187,16 +198,17 @@ func (rb *APIBuilder) Party(relativePath string, handlers ...context.Handler) Pa
}
fullpath := parentPath + relativePath
// append the parent's +child's handlers
// append the parent's + child's handlers
middleware := joinHandlers(rb.middleware, handlers)
return &APIBuilder{
// global/api builder
macros: rb.macros,
routes: rb.routes,
errorCodeHandlers: rb.errorCodeHandlers,
doneHandlers: rb.doneHandlers,
reporter: rb.reporter,
macros: rb.macros,
routes: rb.routes,
errorCodeHandlers: rb.errorCodeHandlers,
beginGlobalHandlers: rb.beginGlobalHandlers,
doneGlobalHandlers: rb.doneGlobalHandlers,
reporter: rb.reporter,
// per-party/children
middleware: middleware,
relativePath: fullpath,
@ -256,6 +268,12 @@ func (rb *APIBuilder) GetRoute(routeName string) *Route {
// Use appends Handler(s) to the current Party's routes and child routes.
// If the current Party is the root, then it registers the middleware to all child Parties' routes too.
//
// Call order matters, it should be called right before the routes that they care about these handlers.
//
// If it's called after the routes then these handlers will never be executed.
// Use `UseGlobal` if you want to register begin handlers(middleware)
// that should be always run before all application's routes.
func (rb *APIBuilder) Use(middleware ...context.Handler) {
rb.middleware = append(rb.middleware, middleware...)
}
@ -263,27 +281,26 @@ func (rb *APIBuilder) Use(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.
func (rb *APIBuilder) Done(handlers ...context.Handler) {
if len(rb.apiRoutes) > 0 { // register these middleware on previous-party-defined routes, it called after the party's route methods (Handle/HandleFunc/Get/Post/Put/Delete/...)
for i, n := 0, len(rb.apiRoutes); i < n; i++ {
routeInfo := rb.apiRoutes[i]
routeInfo.Handlers = append(routeInfo.Handlers, handlers...)
}
} else {
// register them on the doneHandlers, which will be used on Handle to append these middlweare as the last handler(s)
rb.doneHandlers = append(rb.doneHandlers, handlers...)
for _, r := range rb.routes.routes {
r.done(handlers) // append the handlers to the existing routes
}
// set as done handlers for the next routes as well.
rb.doneGlobalHandlers = append(rb.doneGlobalHandlers, handlers...)
}
// UseGlobal registers Handler middleware to the beginning, prepends them instead of append
// UseGlobal registers handlers that should run before all routes,
// including all parties, subdomains
// and other middleware that were registered before or will be after.
// It doesn't care about call order, it will prepend the handlers to all
// existing routes and the future routes that may being registered.
//
// Use it when you want to add a global middleware to all parties, to all routes in all subdomains
// It should be called right before Listen functions
// It's always a good practise to call it right before the `Application#Run` function.
func (rb *APIBuilder) UseGlobal(handlers ...context.Handler) {
for _, r := range rb.routes.routes {
r.Handlers = append(handlers, r.Handlers...) // prepend the handlers
r.use(handlers) // prepend the handlers to the existing routes
}
rb.middleware = append(handlers, rb.middleware...) // set as middleware on the next routes too
// rb.Use(handlers...)
// set as begin handlers for the next routes as well.
rb.beginGlobalHandlers = append(rb.beginGlobalHandlers, handlers...)
}
// None registers an "offline" route

View File

@ -114,6 +114,9 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
rp := errors.NewReporter()
for _, r := range registeredRoutes {
// build the r.Handlers based on begin and done handlers, if any.
r.BuildHandlers()
if r.Subdomain != "" {
h.hosts = true
}

View File

@ -17,7 +17,15 @@ type Route struct {
Subdomain string // "admin."
tmpl *macro.Template // Tmpl().Src: "/api/user/{id:int}"
Path string // "/api/user/:id"
Handlers context.Handlers
// temp storage, they're appended to the Handlers on build.
// Execution happens before Handlers, can be empty.
beginHandlers context.Handlers
// Handlers are the main route's handlers, executed by order.
// Cannot be empty.
Handlers context.Handlers
// temp storage, they're appended to the Handlers on build.
// Execution happens after Begin and main Handler(s), can be empty.
doneHandlers context.Handlers
// FormattedPath all dynamic named parameters (if any) replaced with %v,
// used by Application to validate param values of a Route based on its name.
FormattedPath string
@ -57,6 +65,47 @@ func NewRoute(method, subdomain, unparsedPath string,
return route, nil
}
// use adds explicit begin handlers(middleware) to this route,
// It's being called internally, it's useless for outsiders
// because `Handlers` field is exported.
// The callers of this function are: `APIBuilder#UseGlobal` and `APIBuilder#Done`.
//
// BuildHandlers should be called to build the route's `Handlers`.
func (r *Route) use(handlers context.Handlers) {
if len(handlers) == 0 {
return
}
r.beginHandlers = append(r.beginHandlers, handlers...)
}
// use adds explicit done handlers to this route.
// It's being called internally, it's useless for outsiders
// because `Handlers` field is exported.
// The callers of this function are: `APIBuilder#UseGlobal` and `APIBuilder#Done`.
//
// BuildHandlers should be called to build the route's `Handlers`.
func (r *Route) done(handlers context.Handlers) {
if len(handlers) == 0 {
return
}
r.doneHandlers = append(r.doneHandlers, handlers...)
}
// BuildHandlers is executed automatically by the router handler
// at the `Application#Build` state. Do not call it manually, unless
// you were defined your own request mux handler.
func (r *Route) BuildHandlers() {
if len(r.beginHandlers) > 0 {
r.Handlers = append(r.beginHandlers, r.Handlers...)
r.beginHandlers = r.beginHandlers[0:0]
}
if len(r.doneHandlers) > 0 {
r.Handlers = append(r.Handlers, r.doneHandlers...)
r.doneHandlers = r.doneHandlers[0:0]
} // note: no mutex needed, this should be called in-sync when server is not running of course.
}
// String returns the form of METHOD, SUBDOMAIN, TMPL PATH
func (r Route) String() string {
return fmt.Sprintf("%s %s%s",

View File

@ -0,0 +1,147 @@
// black-box testing
//
// see _examples/routing/main_test.go for the most common router tests that you may want to see,
// this is a test which makes sure that the APIBuilder's `UseGlobal`, `Use` and `Done` functions are
// working as expected.
package router_test
import (
"testing"
"github.com/kataras/iris"
"github.com/kataras/iris/context"
"github.com/kataras/iris/httptest"
)
// test registering of below handlers
// 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)
ctx.Next()
}
firstUseResponse = "use1"
firstUseHandler = func(ctx context.Context) {
ctx.WriteString(firstUseResponse)
ctx.Next()
}
secondUseResponse = "use2"
secondUseHandler = func(ctx context.Context) {
ctx.WriteString(secondUseResponse)
ctx.Next()
}
firstUseGlobalResponse = "useglobal1"
firstUseGlobalHandler = func(ctx context.Context) {
ctx.WriteString(firstUseGlobalResponse)
ctx.Next()
}
secondUseGlobalResponse = "useglobal2"
secondUseGlobalHandler = func(ctx context.Context) {
ctx.WriteString(secondUseGlobalResponse)
ctx.Next()
}
firstDoneResponse = "done1"
firstDoneHandler = func(ctx context.Context) {
ctx.WriteString(firstDoneResponse)
ctx.Next()
}
secondDoneResponse = "done2"
secondDoneHandler = func(ctx context.Context) {
ctx.WriteString(secondDoneResponse)
}
finalResponse = firstUseGlobalResponse + secondUseGlobalResponse +
firstUseResponse + secondUseResponse + mainResponse + firstDoneResponse + secondDoneResponse
testResponse = func(t *testing.T, app *iris.Application, path string) {
e := httptest.New(t, app)
e.GET(path).Expect().Status(httptest.StatusOK).Body().Equal(finalResponse)
}
)
func TestMiddlewareByRouteDef(t *testing.T) {
app := iris.New()
app.Get("/mypath", firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler,
mainHandler, firstDoneHandler, secondDoneHandler)
testResponse(t, app, "/mypath")
}
func TestMiddlewareByUseAndDoneDef(t *testing.T) {
app := iris.New()
app.Use(firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler)
app.Done(firstDoneHandler, secondDoneHandler)
app.Get("/mypath", mainHandler)
testResponse(t, app, "/mypath")
}
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.Get("/mypath", mainHandler)
testResponse(t, app, "/mypath")
}
func TestMiddlewareByUseDoneAndUseGlobalDef(t *testing.T) {
app := iris.New()
app.Use(firstUseHandler, secondUseHandler)
app.Done(firstDoneHandler, secondDoneHandler)
app.Get("/mypath", mainHandler)
// if failed then UseGlobal was unable to
// prepend these handlers to the route was registered before
// OR
// when order failed because these should be executed in order, first the firstUseGlobalHandler,
// because they are the same type (global begin handlers)
app.UseGlobal(firstUseGlobalHandler)
app.UseGlobal(secondUseGlobalHandler)
testResponse(t, app, "/mypath")
}
func TestMiddlewareByUseGlobalUseAndDoneDef(t *testing.T) {
app := iris.New()
app.UseGlobal(firstUseGlobalHandler)
app.UseGlobal(secondUseGlobalHandler)
app.Use(firstUseHandler, secondUseHandler)
app.Get("/mypath", mainHandler)
app.Done(firstDoneHandler, secondDoneHandler)
testResponse(t, app, "/mypath")
}
func TestMiddlewareByDoneUseAndUseGlobalDef(t *testing.T) {
app := iris.New()
app.Done(firstDoneHandler, secondDoneHandler)
app.Use(firstUseHandler, secondUseHandler)
app.Get("/mypath", mainHandler)
app.UseGlobal(firstUseGlobalHandler)
app.UseGlobal(secondUseGlobalHandler)
testResponse(t, app, "/mypath")
}

View File

@ -82,7 +82,7 @@ func TestRouterWildcardAndStatic(t *testing.T) {
}},
}
testTheRoutes(t, tt, true)
testTheRoutes(t, tt, false)
}
func TestRouterWildcardRootMany(t *testing.T) {
@ -108,7 +108,7 @@ func TestRouterWildcardRootMany(t *testing.T) {
}},
}
testTheRoutes(t, tt, true)
testTheRoutes(t, tt, false)
}
func TestRouterWildcardRootManyAndRootStatic(t *testing.T) {
@ -137,7 +137,7 @@ func TestRouterWildcardRootManyAndRootStatic(t *testing.T) {
}},
}
testTheRoutes(t, tt, true)
testTheRoutes(t, tt, false)
}
func testTheRoutes(t *testing.T, tests []testRoute, debug bool) {

2
doc.go
View File

@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
Current Version
8.0.6
8.0.7
Installation

View File

@ -33,7 +33,7 @@ import (
const (
// Version is the current version number of the Iris Web Framework.
Version = "8.0.6"
Version = "8.0.7"
)
// HTTP status codes as registered with IANA.