From 8340285e7d02a7b951cc3a27931f6b069ff487ea Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sat, 15 Aug 2020 12:17:48 +0300 Subject: [PATCH] fix #1588 --- HISTORY.md | 4 ++- _examples/README.md | 1 + .../subdomains/http-errors-view/main.go | 30 +++++++++++++++++ .../http-errors-view/views/error.html | 9 +++++ .../http-errors-view/views/index.html | 3 ++ .../views/layouts/layout.html | 12 +++++++ .../views/layouts/test.layout.html | 10 ++++++ .../http-errors-view/views/partials/404.html | 3 ++ .../http-errors-view/views/partials/500.html | 3 ++ core/router/api_builder.go | 33 ++++++++++++++----- core/router/party.go | 8 +++-- 11 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 _examples/routing/subdomains/http-errors-view/main.go create mode 100644 _examples/routing/subdomains/http-errors-view/views/error.html create mode 100644 _examples/routing/subdomains/http-errors-view/views/index.html create mode 100644 _examples/routing/subdomains/http-errors-view/views/layouts/layout.html create mode 100644 _examples/routing/subdomains/http-errors-view/views/layouts/test.layout.html create mode 100644 _examples/routing/subdomains/http-errors-view/views/partials/404.html create mode 100644 _examples/routing/subdomains/http-errors-view/views/partials/500.html diff --git a/HISTORY.md b/HISTORY.md index d4deb1ac..fe52f7b6 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -395,7 +395,9 @@ func main() { } ``` -- `Application.UseRouter(...Handler)` - per party 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 matched requests. Example of use-case: CORS. +- `Party.UseError(...Handler)` - to register handlers to run before the `OnErrorCode/OnAnyErrorCode` ones. + +- `Party.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 matched requests. Example of use-case: CORS. - `*versioning.Group` type is a full `Party` now. diff --git a/_examples/README.md b/_examples/README.md index 536a7567..2e16a848 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -61,6 +61,7 @@ * [Wildcard](routing/subdomains/wildcard/main.go) * [WWW](routing/subdomains/www/main.go) * [Redirection](routing/subdomains/redirect/main.go) + * [HTTP Errors View](routing/subdomains/http-errors-view/main.go) * [HTTP Method Override](https://github.com/kataras/iris/blob/master/middleware/methodoverride/methodoverride_test.go) * [API Versioning](routing/versioning/main.go) * [Sitemap](routing/sitemap/main.go) diff --git a/_examples/routing/subdomains/http-errors-view/main.go b/_examples/routing/subdomains/http-errors-view/main.go new file mode 100644 index 00000000..0fe25702 --- /dev/null +++ b/_examples/routing/subdomains/http-errors-view/main.go @@ -0,0 +1,30 @@ +package main + +import "github.com/kataras/iris/v12" + +func main() { + newApp().Listen("mydomain.com:80", iris.WithLogLevel("debug")) +} + +func newApp() *iris.Application { + app := iris.New() + + test := app.Subdomain("test") + test.RegisterView(iris.HTML("./views", ".html"). + Layout("layouts/test.layout.html")) + + test.OnErrorCode(iris.StatusNotFound, handleNotFoundTestSubdomain) + test.Get("/", testIndex) + + return app +} + +func handleNotFoundTestSubdomain(ctx iris.Context) { + ctx.View("error.html", iris.Map{ + "ErrorCode": ctx.GetStatusCode(), + }) +} + +func testIndex(ctx iris.Context) { + ctx.Writef("%s index page\n", ctx.Subdomain()) +} diff --git a/_examples/routing/subdomains/http-errors-view/views/error.html b/_examples/routing/subdomains/http-errors-view/views/error.html new file mode 100644 index 00000000..fb015a95 --- /dev/null +++ b/_examples/routing/subdomains/http-errors-view/views/error.html @@ -0,0 +1,9 @@ +
+

Oups, you've got an error!

+ {{ if .ErrorCode }} + {{ $tmplName := print "partials/" .ErrorCode ".html"}} + {{ render $tmplName }} + {{ else }} + {{ render "partials/500.html" }} + {{ end }} +
diff --git a/_examples/routing/subdomains/http-errors-view/views/index.html b/_examples/routing/subdomains/http-errors-view/views/index.html new file mode 100644 index 00000000..736e3395 --- /dev/null +++ b/_examples/routing/subdomains/http-errors-view/views/index.html @@ -0,0 +1,3 @@ +
+ Index Page +
diff --git a/_examples/routing/subdomains/http-errors-view/views/layouts/layout.html b/_examples/routing/subdomains/http-errors-view/views/layouts/layout.html new file mode 100644 index 00000000..e454f1fc --- /dev/null +++ b/_examples/routing/subdomains/http-errors-view/views/layouts/layout.html @@ -0,0 +1,12 @@ + + +Website Layout + + + +

This is the global layout

+
+ + {{ yield }} + + diff --git a/_examples/routing/subdomains/http-errors-view/views/layouts/test.layout.html b/_examples/routing/subdomains/http-errors-view/views/layouts/test.layout.html new file mode 100644 index 00000000..8849bd75 --- /dev/null +++ b/_examples/routing/subdomains/http-errors-view/views/layouts/test.layout.html @@ -0,0 +1,10 @@ + + +Test Subdomain + + + + + {{ yield }} + + diff --git a/_examples/routing/subdomains/http-errors-view/views/partials/404.html b/_examples/routing/subdomains/http-errors-view/views/partials/404.html new file mode 100644 index 00000000..1a621016 --- /dev/null +++ b/_examples/routing/subdomains/http-errors-view/views/partials/404.html @@ -0,0 +1,3 @@ +
+

Not Found

+
diff --git a/_examples/routing/subdomains/http-errors-view/views/partials/500.html b/_examples/routing/subdomains/http-errors-view/views/partials/500.html new file mode 100644 index 00000000..355dace2 --- /dev/null +++ b/_examples/routing/subdomains/http-errors-view/views/partials/500.html @@ -0,0 +1,3 @@ +
+

Internal Server Error

+
diff --git a/core/router/api_builder.go b/core/router/api_builder.go index a781f47c..b692c6bc 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -173,7 +173,8 @@ type APIBuilder struct { // the per-party handlers, order // of handlers registration matters. - middleware context.Handlers + middleware context.Handlers + middlewareErrorCode context.Handlers // 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 @@ -539,6 +540,8 @@ func (api *APIBuilder) createRoutes(errorCode int, methods []string, relativePat if errorCode == 0 { beginHandlers = context.JoinHandlers(beginHandlers, api.middleware) doneHandlers = context.JoinHandlers(doneHandlers, api.doneHandlers) + } else { + beginHandlers = context.JoinHandlers(beginHandlers, api.middlewareErrorCode) } mainHandlers := context.Handlers(handlers) @@ -549,7 +552,7 @@ func (api *APIBuilder) createRoutes(errorCode int, methods []string, relativePat mainHandlerFileName, mainHandlerFileNumber := context.HandlerFileLineRel(handlers[mainHandlerIndex]) // re-calculate mainHandlerIndex in favor of the middlewares. - mainHandlerIndex = len(api.middleware) + len(api.beginGlobalHandlers) + mainHandlerIndex + mainHandlerIndex = len(beginHandlers) + mainHandlerIndex // TODO: for UseGlobal/DoneGlobal that doesn't work. applyExecutionRules(api.handlerExecutionRules, &beginHandlers, &doneHandlers, &mainHandlers) @@ -668,6 +671,7 @@ func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) P // per-party/children parent: api, middleware: middleware, + middlewareErrorCode: context.JoinHandlers(api.middlewareErrorCode, context.Handlers{}), doneHandlers: api.doneHandlers[0:], relativePath: fullpath, allowMethods: allowMethods, @@ -938,6 +942,13 @@ func (api *APIBuilder) UseRouter(handlers ...context.Handler) { } } +// UseError upserts one or more handlers that will be fired, +// as middleware, before any error handler registered through `On(Any)ErrorCode`. +// See `OnErrorCode` too. +func (api *APIBuilder) UseError(handlers ...context.Handler) { + api.middlewareErrorCode = context.UpsertHandlers(api.middlewareErrorCode, handlers) +} + // 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. // @@ -1011,6 +1022,7 @@ func (api *APIBuilder) DoneGlobal(handlers ...context.Handler) { // Returns this Party. func (api *APIBuilder) Reset() Party { api.middleware = api.middleware[0:0] + api.middlewareErrorCode = api.middlewareErrorCode[0:0] api.doneHandlers = api.doneHandlers[0:0] api.handlerExecutionRules = ExecutionRules{} api.routeRegisterRule = RouteOverride @@ -1189,7 +1201,7 @@ func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route { // OnErrorCode registers a handlers chain for this `Party` for a specific HTTP status code. // Read more at: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml -// Look `OnAnyErrorCode` too. +// Look `UseError` and `OnAnyErrorCode` too. func (api *APIBuilder) OnErrorCode(statusCode int, handlers ...context.Handler) (routes []*Route) { routes = append(routes, api.handle(statusCode, "", "/", handlers...)) @@ -1202,7 +1214,7 @@ func (api *APIBuilder) OnErrorCode(statusCode int, handlers ...context.Handler) // OnAnyErrorCode registers a handlers chain for all error codes // (4xxx and 5xxx, change the `context.ClientErrorCodes` and `context.ServerErrorCodes` variables to modify those) -// Look `OnErrorCode` too. +// Look `UseError` and `OnErrorCode` too. func (api *APIBuilder) OnAnyErrorCode(handlers ...context.Handler) (routes []*Route) { for _, statusCode := range context.ClientAndServerErrorCodes { routes = append(routes, api.OnErrorCode(statusCode, handlers...)...) @@ -1221,10 +1233,12 @@ func (api *APIBuilder) RegisterView(viewEngine context.ViewEngine) { return } - api.Use(func(ctx *context.Context) { + handler := func(ctx *context.Context) { ctx.ViewEngine(viewEngine) ctx.Next() - }) + } + api.Use(handler) + api.UseError(handler) // Note (@kataras): It does not return the Party in order // to keep the iris.Application a compatible Party. } @@ -1244,10 +1258,13 @@ func (api *APIBuilder) RegisterView(viewEngine context.ViewEngine) { // // Examples: https://github.com/kataras/iris/tree/master/_examples/view func (api *APIBuilder) Layout(tmplLayoutFile string) Party { - api.Use(func(ctx *context.Context) { + handler := func(ctx *context.Context) { ctx.ViewLayout(tmplLayoutFile) ctx.Next() - }) + } + + api.Use(handler) + api.UseError(handler) return api } diff --git a/core/router/party.go b/core/router/party.go index 4051ea49..227433a5 100644 --- a/core/router/party.go +++ b/core/router/party.go @@ -41,11 +41,11 @@ type Party interface { // OnErrorCode registers a handlers chain for this `Party` for a specific HTTP status code. // Read more at: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml - // Look `OnAnyErrorCode` too. + // Look `UseError` and `OnAnyErrorCode` too. OnErrorCode(statusCode int, handlers ...context.Handler) []*Route // OnAnyErrorCode registers a handlers chain for all error codes // (4xxx and 5xxx, change the `ClientErrorCodes` and `ServerErrorCodes` variables to modify those) - // Look `OnErrorCode` too. + // Look `UseError` and `OnErrorCode` too. OnAnyErrorCode(handlers ...context.Handler) []*Route // Party returns a new child Party which inherites its @@ -96,6 +96,10 @@ type Party interface { // The context SHOULD call its `Next` method in order to proceed to // the next handler in the chain or the main request handler one. UseRouter(handlers ...context.Handler) + // UseError upserts one or more handlers that will be fired, + // as middleware, before any error handler registered through `On(Any)ErrorCode`. + // See `OnErrorCode` too. + UseError(handlers ...context.Handler) // 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. Use(middleware ...context.Handler)