add 'Configuration.PathIntelligence' and 'OnErrorCode' and 'OnAnyErrorCode' on APIContainer

Former-commit-id: bc3d0232106622063205f326bfa4ed3aa84179de
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-05-17 08:10:07 +03:00
parent 21a013569f
commit 1e20996330
10 changed files with 104 additions and 27 deletions

View File

@ -371,6 +371,8 @@ Other Improvements:
![DBUG routes](https://iris-go.com/images/v12.2.0-dbug2.png?v=0)
- `Configuration.EnablePathIntelligence | iris.WithPathIntelligence` to enable path intelligence automatic path redirection on the most closest path (if any), [example]((https://github.com/kataras/iris/blob/master/_examples/routing/intelligence/main.go)
- Enhanced cookie security and management through new `Context.AddCookieOptions` method and new cookie options (look on New Package-level functions section below), [securecookie](https://github.com/kataras/iris/tree/master/_examples/cookies/securecookie) example has been updated.
- `Context.RemoveCookie` removes also the Request's specific cookie of the same request lifecycle when `iris.CookieAllowReclaim` is set to cookie options, [example](https://github.com/kataras/iris/tree/master/_examples/cookies/options).
@ -494,7 +496,7 @@ Fix [[BUG]Session works incorrectly when meets the multi-level TLDs](https://git
# Mo, 16 December 2019 | v12.1.1
Add [Context.FindClosest(n int) []string](https://github.com/kataras/iris/blob/master/_examples/routing/not-found-suggests/main.go#L22)
Add [Context.FindClosest(n int) []string](https://github.com/kataras/iris/blob/master/_examples/routing/intelligence/manual/main.go#L22)
```go
app := iris.New()

View File

@ -36,7 +36,8 @@
* [Overview](routing/overview/main.go)
* [Basic](routing/basic/main.go)
* [Custom HTTP Errors](routing/http-errors/main.go)
* [Not Found - Suggest Closest Paths](routing/not-found-suggests/main.go)
* [Not Found - Intelligence](routing/intelligence/main.go)
* [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go)
* [Dynamic Path](routing/dynamic-path/main.go)
* [Root Wildcard](routing/dynamic-path/root-wildcard/main.go)
* [Implement a Parameter Type](routing/macros/main.go)

View File

@ -0,0 +1,24 @@
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.New()
app.Get("/home", handler)
app.Get("/contact", handler)
app.Get("/contract", handler)
// http://localhost:8080/home
// http://localhost:8080/hom
//
// http://localhost:8080/contact
// http://localhost:8080/cont
//
// http://localhost:8080/contract
// http://localhost:8080/contr
app.Listen(":8080", iris.WithPathIntelligence)
}
func handler(ctx iris.Context) {
ctx.Writef("Path: %s", ctx.Path())
}

View File

@ -240,6 +240,13 @@ var WithoutPathCorrection = func(app *Application) {
app.config.DisablePathCorrection = true
}
// WithPathIntelligence enables the EnablePathIntelligence setting.
//
// See `Configuration`.
var WithPathIntelligence = func(app *Application) {
app.config.EnablePathIntelligence = true
}
// WithoutPathCorrectionRedirection disables the PathCorrectionRedirection setting.
//
// See `Configuration`.
@ -781,14 +788,21 @@ type Configuration struct {
//
// Defaults to false.
DisablePathCorrection bool `json:"disablePathCorrection,omitempty" yaml:"DisablePathCorrection" toml:"DisablePathCorrection"`
// DisablePathCorrectionRedirection works whenever configuration.DisablePathCorrection is set to false
// and if DisablePathCorrectionRedirection set to true then it will fire the handler of the matching route without
// the trailing slash ("/") instead of send a redirection status.
//
// Defaults to false.
DisablePathCorrectionRedirection bool `json:"disablePathCorrectionRedirection,omitempty" yaml:"DisablePathCorrectionRedirection" toml:"DisablePathCorrectionRedirection"`
// EnablePathIntelligence if set to true,
// the router will redirect HTTP "GET" not found pages to the most closest one path(if any). For example
// you register a route at "/contact" path -
// a client tries to reach it by "/cont", the path will be automatic fixed
// and the client will be redirected to the "/contact" path
// instead of getting a 404 not found response back.
//
// Defaults to false.
EnablePathIntelligence bool `json:"enablePathIntelligence,omitempty" yaml:"EnablePathIntelligence" toml:"EnablePathIntelligence"`
// EnablePathEscape when is true then its escapes the path and the named parameters (if any).
// When do you need to Disable(false) it:
// accepts parameters with slash '/'
@ -799,23 +813,21 @@ type Configuration struct {
//
// Defaults to false.
EnablePathEscape bool `json:"enablePathEscape,omitempty" yaml:"EnablePathEscape" toml:"EnablePathEscape"`
// ForceLowercaseRouting if enabled, converts all registered routes paths to lowercase
// and it does lowercase the request path too for matching.
//
// Defaults to false.
ForceLowercaseRouting bool `json:"forceLowercaseRouting,omitempty" yaml:"ForceLowercaseRouting" toml:"ForceLowercaseRouting"`
// FireMethodNotAllowed if it's true router checks for StatusMethodNotAllowed(405) and
// fires the 405 error instead of 404
// Defaults to false.
FireMethodNotAllowed bool `json:"fireMethodNotAllowed,omitempty" yaml:"FireMethodNotAllowed" toml:"FireMethodNotAllowed"`
// EnableOptimization when this field is true
// then the application tries to optimize for the best performance where is possible.
//
// Defaults to false.
EnableOptimizations bool `json:"enableOptimizations,omitempty" yaml:"EnableOptimizations" toml:"EnableOptimizations"`
// FireMethodNotAllowed if it's true router checks for StatusMethodNotAllowed(405) and
// fires the 405 error instead of 404
// Defaults to false.
FireMethodNotAllowed bool `json:"fireMethodNotAllowed,omitempty" yaml:"FireMethodNotAllowed" toml:"FireMethodNotAllowed"`
// DisableBodyConsumptionOnUnmarshal manages the reading behavior of the context's body readers/binders.
// If set to true then it
// disables the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`.
@ -962,35 +974,40 @@ func (c Configuration) GetDisablePathCorrection() bool {
return c.DisablePathCorrection
}
// GetDisablePathCorrectionRedirection returns the Configuration#DisablePathCorrectionRedirection field.
// GetDisablePathCorrectionRedirection returns the Configuration.DisablePathCorrectionRedirection field.
// If DisablePathCorrectionRedirection set to true then it will fire the handler of the matching route without
// the last slash ("/") instead of send a redirection status.
func (c Configuration) GetDisablePathCorrectionRedirection() bool {
return c.DisablePathCorrectionRedirection
}
// GetEnablePathEscape is the Configuration#EnablePathEscape,
// GetEnablePathIntelligence returns the Configuration.EnablePathIntelligence field.
func (c Configuration) GetEnablePathIntelligence() bool {
return c.EnablePathIntelligence
}
// GetEnablePathEscape is the Configuration.EnablePathEscape,
// returns true when its escapes the path, the named parameters (if any).
func (c Configuration) GetEnablePathEscape() bool {
return c.EnablePathEscape
}
// GetForceLowercaseRouting returns the value of the `ForceLowercaseRouting` setting.
// GetForceLowercaseRouting returns the value of the Configuration.ForceLowercaseRouting setting.
func (c Configuration) GetForceLowercaseRouting() bool {
return c.ForceLowercaseRouting
}
// GetFireMethodNotAllowed returns the Configuration.FireMethodNotAllowed.
func (c Configuration) GetFireMethodNotAllowed() bool {
return c.FireMethodNotAllowed
}
// GetEnableOptimizations returns whether
// the application has performance optimizations enabled.
func (c Configuration) GetEnableOptimizations() bool {
return c.EnableOptimizations
}
// GetFireMethodNotAllowed returns the Configuration#FireMethodNotAllowed.
func (c Configuration) GetFireMethodNotAllowed() bool {
return c.FireMethodNotAllowed
}
// GetDisableBodyConsumptionOnUnmarshal returns the Configuration#GetDisableBodyConsumptionOnUnmarshal,
// manages the reading behavior of the context's body readers/binders.
// If returns true then the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`
@ -1149,6 +1166,10 @@ func WithConfiguration(c Configuration) Configurator {
main.DisablePathCorrectionRedirection = v
}
if v := c.EnablePathIntelligence; v {
main.EnablePathIntelligence = v
}
if v := c.EnablePathEscape; v {
main.EnablePathEscape = v
}

View File

@ -146,6 +146,7 @@ func TestConfigurationYAML(t *testing.T) {
yamlConfigurationContents := `
DisablePathCorrection: false
DisablePathCorrectionRedirection: true
EnablePathIntelligence: true
EnablePathEscape: false
FireMethodNotAllowed: true
EnableOptimizations: true
@ -175,6 +176,10 @@ Other:
t.Fatalf("error on TestConfigurationYAML: Expected DisablePathCorrectionRedirection %v but got %v", expected, c.DisablePathCorrectionRedirection)
}
if expected := true; c.EnablePathIntelligence != expected {
t.Fatalf("error on TestConfigurationYAML: Expected EnablePathIntelligence %v but got %v", expected, c.EnablePathIntelligence)
}
if expected := false; c.EnablePathEscape != expected {
t.Fatalf("error on TestConfigurationYAML: Expected EnablePathEscape %v but got %v", expected, c.EnablePathEscape)
}

View File

@ -25,26 +25,24 @@ type ConfigurationReadOnly interface {
// then the Router checks if /home handler exists, if yes,
// (permant)redirects the client to the correct path /home.
GetDisablePathCorrection() bool
// GetDisablePathCorrectionRedirection returns the Configuration#DisablePathCorrectionRedirection field.
// GetDisablePathCorrectionRedirection returns the Configuration.DisablePathCorrectionRedirection field.
// If DisablePathCorrectionRedirection set to true then it will handle paths as they are.
// it will fire the handler of the matching route without
// the last slash ("/") instead of send a redirection status.
GetDisablePathCorrectionRedirection() bool
// GetEnablePathIntelligence returns the Configuration.EnablePathIntelligence field.
GetEnablePathIntelligence() bool
// GetEnablePathEscape is the configuration.EnablePathEscape,
// returns true when its escapes the path, the named parameters (if any).
GetEnablePathEscape() bool
// GetForceLowercaseRouting returns the value of the `ForceLowercaseRouting` setting.
GetForceLowercaseRouting() bool
// GetFireMethodNotAllowed returns the configuration.FireMethodNotAllowed.
GetFireMethodNotAllowed() bool
// GetEnableOptimizations returns whether
// the application has performance optimizations enabled.
GetEnableOptimizations() bool
// GetFireMethodNotAllowed returns the configuration.FireMethodNotAllowed.
GetFireMethodNotAllowed() bool
// GetDisableBodyConsumptionOnUnmarshal returns the configuration.GetDisableBodyConsumptionOnUnmarshal,
// manages the reading behavior of the context's body readers/binders.
// If returns true then the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`

View File

@ -351,7 +351,7 @@ type Context interface {
// this request based on subdomain and request path.
//
// Order may change.
// Example: https://github.com/kataras/iris/tree/master/_examples/routing/not-found-suggests
// Example: https://github.com/kataras/iris/tree/master/_examples/routing/intelligence/manual
FindClosest(n int) []string
// IsWWW returns true if the current subdomain (if any) is www.
IsWWW() bool
@ -1812,7 +1812,7 @@ func (ctx *context) Subdomain() (subdomain string) {
// this request based on subdomain and request path.
//
// Order may change.
// Example: https://github.com/kataras/iris/tree/master/_examples/routing/not-found-suggests
// Example: https://github.com/kataras/iris/tree/master/_examples/routing/intelligence/manual
func (ctx *context) FindClosest(n int) []string {
return ctx.Application().FindClosestPaths(ctx.Subdomain(), ctx.Path(), n)
}

View File

@ -229,3 +229,19 @@ func (api *APIContainer) Any(relativePath string, handlersFn ...interface{}) (ro
return
}
// 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.
func (api *APIContainer) OnErrorCode(statusCode int, handlersFn ...interface{}) []*Route {
handlers := api.convertHandlerFuncs("/{tail:path}", handlersFn...)
return api.Self.OnErrorCode(statusCode, handlers...)
}
// OnAnyErrorCode registers a handlers chain for all error codes
// (4xxx and 5xxx, change the `ClientErrorCodes` and `ServerErrorCodes` variables to modify those)
// Look `OnErrorCode` too.
func (api *APIContainer) OnAnyErrorCode(handlersFn ...interface{}) []*Route {
handlers := api.convertHandlerFuncs("/{tail:path}", handlersFn...)
return api.Self.OnAnyErrorCode(handlers...)
}

View File

@ -417,6 +417,16 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
}
}
if config.GetEnablePathIntelligence() && method == http.MethodGet {
closestPaths := ctx.FindClosest(1)
if len(closestPaths) > 0 {
u := ctx.Request().URL
u.Path = closestPaths[0]
ctx.Redirect(u.String(), http.StatusMovedPermanently)
return
}
}
ctx.StatusCode(http.StatusNotFound)
}