1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-17 18:07:01 +00:00

HTTP error handlers per Party (docs and details in progress)

Former-commit-id: 7092ebed556b56d9f1769b9b23f2340c2a3a18f7
This commit is contained in:
Gerasimos (Makis) Maropoulos
2020-05-11 00:44:54 +03:00
parent 3657aaf240
commit c039730521
18 changed files with 434 additions and 306 deletions

View File

@@ -58,7 +58,7 @@ func (repo *repository) getRelative(r *Route) *Route {
}
for _, route := range repo.routes {
if r.Subdomain == route.Subdomain && r.Method == route.Method && r.FormattedPath == route.FormattedPath && !route.tmpl.IsTrailing() {
if r.Subdomain == route.Subdomain && r.StatusCode == route.StatusCode && r.Method == route.Method && r.FormattedPath == route.FormattedPath && !route.tmpl.IsTrailing() {
return route
}
}
@@ -100,12 +100,16 @@ func (repo *repository) register(route *Route, rule RouteRegisterRule) (*Route,
}
}
// fmt.Printf("repo.routes append:\t%#+v\n\n", route)
repo.routes = append(repo.routes, route)
if repo.pos == nil {
repo.pos = make(map[string]int)
if route.StatusCode == 0 { // a common resource route, not a status code error handler.
if repo.pos == nil {
repo.pos = make(map[string]int)
}
repo.pos[route.tmpl.Src] = len(repo.routes) - 1
}
repo.pos[route.tmpl.Src] = len(repo.routes) - 1
return route, nil
}
@@ -117,8 +121,6 @@ type APIBuilder struct {
// the api builder global macros registry
macros *macro.Macros
// the api builder global handlers per status code registry (used for custom http errors)
errorCodeHandlers *ErrorCodeHandlers
// the api builder global routes repository
routes *repository
@@ -164,12 +166,11 @@ var _ RoutesProvider = (*APIBuilder)(nil) // passed to the default request handl
// which is responsible to build the API and the router handler.
func NewAPIBuilder() *APIBuilder {
return &APIBuilder{
macros: macro.Defaults,
errorCodeHandlers: defaultErrorCodeHandlers(),
errors: errgroup.New("API Builder"),
relativePath: "/",
routes: new(repository),
apiBuilderDI: &APIContainer{Container: hero.New()},
macros: macro.Defaults,
errors: errgroup.New("API Builder"),
relativePath: "/",
routes: new(repository),
apiBuilderDI: &APIContainer{Container: hero.New()},
}
}
@@ -274,7 +275,11 @@ func (api *APIBuilder) SetRegisterRule(rule RouteRegisterRule) Party {
//
// Returns a *Route, app will throw any errors later on.
func (api *APIBuilder) Handle(method string, relativePath string, handlers ...context.Handler) *Route {
routes := api.CreateRoutes([]string{method}, relativePath, handlers...)
return api.handle(0, method, relativePath, handlers...)
}
func (api *APIBuilder) handle(errorCode int, method string, relativePath string, handlers ...context.Handler) *Route {
routes := api.createRoutes(errorCode, []string{method}, relativePath, handlers...)
var route *Route // the last one is returned.
var err error
@@ -282,6 +287,7 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
if route == nil {
break
}
// global
route.topLink = api.routes.getRelative(route)
@@ -417,8 +423,18 @@ func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptio
// This method can be used for third-parties Iris helpers packages and tools
// that want a more detailed view of Party-based Routes before take the decision to register them.
func (api *APIBuilder) CreateRoutes(methods []string, relativePath string, handlers ...context.Handler) []*Route {
if len(methods) == 0 || methods[0] == "ALL" || methods[0] == "ANY" { // then use like it was .Any
return api.Any(relativePath, handlers...)
return api.createRoutes(0, methods, relativePath, handlers...)
}
func (api *APIBuilder) createRoutes(errorCode int, methods []string, relativePath string, handlers ...context.Handler) []*Route {
if statusCodeSuccessful(errorCode) {
errorCode = 0
}
if errorCode == 0 {
if len(methods) == 0 || methods[0] == "ALL" || methods[0] == "ANY" { // then use like it was .Any
return api.Any(relativePath, handlers...)
}
}
// no clean path yet because of subdomain indicator/separator which contains a dot.
@@ -444,11 +460,17 @@ func (api *APIBuilder) CreateRoutes(methods []string, relativePath string, handl
// So if we just put `api.middleware` or `api.doneHandlers`
// then the next `Party` will have those updated handlers
// but dev may change the rules for that child Party, so we have to make clones of them here.
var (
beginHandlers = joinHandlers(api.middleware, context.Handlers{})
doneHandlers = joinHandlers(api.doneHandlers, context.Handlers{})
beginHandlers context.Handlers
doneHandlers context.Handlers
)
if errorCode == 0 {
beginHandlers = joinHandlers(api.middleware, beginHandlers)
doneHandlers = joinHandlers(api.doneHandlers, doneHandlers)
}
mainHandlers := context.Handlers(handlers)
// before join the middleware + handlers + done handlers and apply the execution rules.
@@ -476,8 +498,8 @@ func (api *APIBuilder) CreateRoutes(methods []string, relativePath string, handl
routes := make([]*Route, len(methods))
for i, m := range methods {
route, err := NewRoute(m, subdomain, path, routeHandlers, *api.macros)
for i, m := range methods { // single, empty method for error handlers.
route, err := NewRoute(errorCode, m, subdomain, path, routeHandlers, *api.macros)
if err != nil { // template path parser errors:
api.errors.Addf("[%s:%d] %v -> %s:%s:%s", filename, line, err, m, subdomain, path)
continue
@@ -523,6 +545,13 @@ func removeDuplicates(elements []string) (result []string) {
//
// You can even declare a subdomain with relativePath as "mysub." or see `Subdomain`.
func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) Party {
// if app.Party("/"), root party, then just add the middlewares
// and return itself.
if api.relativePath == "/" && (relativePath == "" || relativePath == "/") {
api.Use(handlers...)
return api
}
parentPath := api.relativePath
dot := string(SubdomainPrefix[0])
if len(parentPath) > 0 && parentPath[0] == '/' && strings.HasSuffix(relativePath, dot) {
@@ -557,7 +586,6 @@ func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) P
// global/api builder
macros: api.macros,
routes: api.routes,
errorCodeHandlers: api.errorCodeHandlers,
beginGlobalHandlers: api.beginGlobalHandlers,
doneGlobalHandlers: api.doneGlobalHandlers,
errors: api.errors,
@@ -674,7 +702,7 @@ func (api *APIBuilder) GetRoutesReadOnly() []context.RouteReadOnly {
routes := api.GetRoutes()
readOnlyRoutes := make([]context.RouteReadOnly, len(routes))
for i, r := range routes {
readOnlyRoutes[i] = routeReadOnlyWrapper{r}
readOnlyRoutes[i] = r.ReadOnly
}
return readOnlyRoutes
@@ -692,7 +720,7 @@ func (api *APIBuilder) GetRouteReadOnly(routeName string) context.RouteReadOnly
if r == nil {
return nil
}
return routeReadOnlyWrapper{r}
return r.ReadOnly
}
// GetRouteReadOnlyByPath returns the registered read-only route based on the template path (`Route.Tmpl().Src`).
@@ -702,7 +730,7 @@ func (api *APIBuilder) GetRouteReadOnlyByPath(tmplPath string) context.RouteRead
return nil
}
return routeReadOnlyWrapper{r}
return r.ReadOnly
}
// Use appends Handler(s) to the current Party's routes and child routes.
@@ -951,11 +979,25 @@ func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route {
// and/or disable the gzip if gzip response recorder
// was active.
func (api *APIBuilder) OnErrorCode(statusCode int, handlers ...context.Handler) {
if len(api.beginGlobalHandlers) > 0 {
handlers = joinHandlers(api.beginGlobalHandlers, handlers)
// TODO: think a stable way for that and document it so end-developers
// not be suprised. Many feature requests in the past asked for that per-party error handlers.
if api.relativePath != "/" {
api.handle(statusCode, "", "/{tail:path}", handlers...)
}
api.errorCodeHandlers.Register(statusCode, handlers...)
api.handle(statusCode, "", "/", handlers...)
// if api.relativePath != "/" /* root is OK, no need to wildcard, see handler.go */ &&
// !strings.HasSuffix(api.relativePath, "}") /* and not /users/{id:int} */ {
// // We need to register the /users and the /users/{tail:path},
// api.handle(statusCode, "", "/{tail:path}", handlers...)
// }
// if strings.HasSuffix(api.relativePath, "/") {
// api.handle(statusCode, "", "/", handlers...)
// }
// api.handle(statusCode, "", "/{tail:path}", handlers...)
}
// OnAnyErrorCode registers a handler which called when error status code written.
@@ -972,15 +1014,6 @@ func (api *APIBuilder) OnAnyErrorCode(handlers ...context.Handler) {
}
}
// FireErrorCode executes an error http status code handler
// based on the context's status code.
//
// If a handler is not already registered,
// it creates and registers a new trivial handler on the-fly.
func (api *APIBuilder) FireErrorCode(ctx context.Context) {
api.errorCodeHandlers.Fire(ctx)
}
// Layout overrides the parent template layout with a more specific layout for this Party.
// It returns the current Party.
//