mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
add 'Configuration.PathIntelligence' and 'OnErrorCode' and 'OnAnyErrorCode' on APIContainer
Former-commit-id: bc3d0232106622063205f326bfa4ed3aa84179de
This commit is contained in:
parent
21a013569f
commit
1e20996330
|
@ -371,6 +371,8 @@ Other Improvements:
|
||||||
|
|
||||||
![DBUG routes](https://iris-go.com/images/v12.2.0-dbug2.png?v=0)
|
![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.
|
- 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).
|
- `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
|
# 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
|
```go
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
|
|
|
@ -36,7 +36,8 @@
|
||||||
* [Overview](routing/overview/main.go)
|
* [Overview](routing/overview/main.go)
|
||||||
* [Basic](routing/basic/main.go)
|
* [Basic](routing/basic/main.go)
|
||||||
* [Custom HTTP Errors](routing/http-errors/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)
|
* [Dynamic Path](routing/dynamic-path/main.go)
|
||||||
* [Root Wildcard](routing/dynamic-path/root-wildcard/main.go)
|
* [Root Wildcard](routing/dynamic-path/root-wildcard/main.go)
|
||||||
* [Implement a Parameter Type](routing/macros/main.go)
|
* [Implement a Parameter Type](routing/macros/main.go)
|
||||||
|
|
24
_examples/routing/intelligence/main.go
Normal file
24
_examples/routing/intelligence/main.go
Normal 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())
|
||||||
|
}
|
|
@ -240,6 +240,13 @@ var WithoutPathCorrection = func(app *Application) {
|
||||||
app.config.DisablePathCorrection = true
|
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.
|
// WithoutPathCorrectionRedirection disables the PathCorrectionRedirection setting.
|
||||||
//
|
//
|
||||||
// See `Configuration`.
|
// See `Configuration`.
|
||||||
|
@ -781,14 +788,21 @@ type Configuration struct {
|
||||||
//
|
//
|
||||||
// Defaults to false.
|
// Defaults to false.
|
||||||
DisablePathCorrection bool `json:"disablePathCorrection,omitempty" yaml:"DisablePathCorrection" toml:"DisablePathCorrection"`
|
DisablePathCorrection bool `json:"disablePathCorrection,omitempty" yaml:"DisablePathCorrection" toml:"DisablePathCorrection"`
|
||||||
|
|
||||||
// DisablePathCorrectionRedirection works whenever configuration.DisablePathCorrection is set to false
|
// 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
|
// 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.
|
// the trailing slash ("/") instead of send a redirection status.
|
||||||
//
|
//
|
||||||
// Defaults to false.
|
// Defaults to false.
|
||||||
DisablePathCorrectionRedirection bool `json:"disablePathCorrectionRedirection,omitempty" yaml:"DisablePathCorrectionRedirection" toml:"DisablePathCorrectionRedirection"`
|
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).
|
// EnablePathEscape when is true then its escapes the path and the named parameters (if any).
|
||||||
// When do you need to Disable(false) it:
|
// When do you need to Disable(false) it:
|
||||||
// accepts parameters with slash '/'
|
// accepts parameters with slash '/'
|
||||||
|
@ -799,23 +813,21 @@ type Configuration struct {
|
||||||
//
|
//
|
||||||
// Defaults to false.
|
// Defaults to false.
|
||||||
EnablePathEscape bool `json:"enablePathEscape,omitempty" yaml:"EnablePathEscape" toml:"EnablePathEscape"`
|
EnablePathEscape bool `json:"enablePathEscape,omitempty" yaml:"EnablePathEscape" toml:"EnablePathEscape"`
|
||||||
|
|
||||||
// ForceLowercaseRouting if enabled, converts all registered routes paths to lowercase
|
// ForceLowercaseRouting if enabled, converts all registered routes paths to lowercase
|
||||||
// and it does lowercase the request path too for matching.
|
// and it does lowercase the request path too for matching.
|
||||||
//
|
//
|
||||||
// Defaults to false.
|
// Defaults to false.
|
||||||
ForceLowercaseRouting bool `json:"forceLowercaseRouting,omitempty" yaml:"ForceLowercaseRouting" toml:"ForceLowercaseRouting"`
|
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
|
// EnableOptimization when this field is true
|
||||||
// then the application tries to optimize for the best performance where is possible.
|
// then the application tries to optimize for the best performance where is possible.
|
||||||
//
|
//
|
||||||
// Defaults to false.
|
// Defaults to false.
|
||||||
EnableOptimizations bool `json:"enableOptimizations,omitempty" yaml:"EnableOptimizations" toml:"EnableOptimizations"`
|
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.
|
// DisableBodyConsumptionOnUnmarshal manages the reading behavior of the context's body readers/binders.
|
||||||
// If set to true then it
|
// If set to true then it
|
||||||
// disables the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`.
|
// disables the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`.
|
||||||
|
@ -962,35 +974,40 @@ func (c Configuration) GetDisablePathCorrection() bool {
|
||||||
return c.DisablePathCorrection
|
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
|
// 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.
|
// the last slash ("/") instead of send a redirection status.
|
||||||
func (c Configuration) GetDisablePathCorrectionRedirection() bool {
|
func (c Configuration) GetDisablePathCorrectionRedirection() bool {
|
||||||
return c.DisablePathCorrectionRedirection
|
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).
|
// returns true when its escapes the path, the named parameters (if any).
|
||||||
func (c Configuration) GetEnablePathEscape() bool {
|
func (c Configuration) GetEnablePathEscape() bool {
|
||||||
return c.EnablePathEscape
|
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 {
|
func (c Configuration) GetForceLowercaseRouting() bool {
|
||||||
return c.ForceLowercaseRouting
|
return c.ForceLowercaseRouting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFireMethodNotAllowed returns the Configuration.FireMethodNotAllowed.
|
||||||
|
func (c Configuration) GetFireMethodNotAllowed() bool {
|
||||||
|
return c.FireMethodNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
// GetEnableOptimizations returns whether
|
// GetEnableOptimizations returns whether
|
||||||
// the application has performance optimizations enabled.
|
// the application has performance optimizations enabled.
|
||||||
func (c Configuration) GetEnableOptimizations() bool {
|
func (c Configuration) GetEnableOptimizations() bool {
|
||||||
return c.EnableOptimizations
|
return c.EnableOptimizations
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFireMethodNotAllowed returns the Configuration#FireMethodNotAllowed.
|
|
||||||
func (c Configuration) GetFireMethodNotAllowed() bool {
|
|
||||||
return c.FireMethodNotAllowed
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDisableBodyConsumptionOnUnmarshal returns the Configuration#GetDisableBodyConsumptionOnUnmarshal,
|
// GetDisableBodyConsumptionOnUnmarshal returns the Configuration#GetDisableBodyConsumptionOnUnmarshal,
|
||||||
// manages the reading behavior of the context's body readers/binders.
|
// manages the reading behavior of the context's body readers/binders.
|
||||||
// If returns true then the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`
|
// 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
|
main.DisablePathCorrectionRedirection = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v := c.EnablePathIntelligence; v {
|
||||||
|
main.EnablePathIntelligence = v
|
||||||
|
}
|
||||||
|
|
||||||
if v := c.EnablePathEscape; v {
|
if v := c.EnablePathEscape; v {
|
||||||
main.EnablePathEscape = v
|
main.EnablePathEscape = v
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,6 +146,7 @@ func TestConfigurationYAML(t *testing.T) {
|
||||||
yamlConfigurationContents := `
|
yamlConfigurationContents := `
|
||||||
DisablePathCorrection: false
|
DisablePathCorrection: false
|
||||||
DisablePathCorrectionRedirection: true
|
DisablePathCorrectionRedirection: true
|
||||||
|
EnablePathIntelligence: true
|
||||||
EnablePathEscape: false
|
EnablePathEscape: false
|
||||||
FireMethodNotAllowed: true
|
FireMethodNotAllowed: true
|
||||||
EnableOptimizations: true
|
EnableOptimizations: true
|
||||||
|
@ -175,6 +176,10 @@ Other:
|
||||||
t.Fatalf("error on TestConfigurationYAML: Expected DisablePathCorrectionRedirection %v but got %v", expected, c.DisablePathCorrectionRedirection)
|
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 {
|
if expected := false; c.EnablePathEscape != expected {
|
||||||
t.Fatalf("error on TestConfigurationYAML: Expected EnablePathEscape %v but got %v", expected, c.EnablePathEscape)
|
t.Fatalf("error on TestConfigurationYAML: Expected EnablePathEscape %v but got %v", expected, c.EnablePathEscape)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,26 +25,24 @@ type ConfigurationReadOnly interface {
|
||||||
// then the Router checks if /home handler exists, if yes,
|
// then the Router checks if /home handler exists, if yes,
|
||||||
// (permant)redirects the client to the correct path /home.
|
// (permant)redirects the client to the correct path /home.
|
||||||
GetDisablePathCorrection() bool
|
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.
|
// If DisablePathCorrectionRedirection set to true then it will handle paths as they are.
|
||||||
// it will fire the handler of the matching route without
|
// it will fire the handler of the matching route without
|
||||||
// the last slash ("/") instead of send a redirection status.
|
// the last slash ("/") instead of send a redirection status.
|
||||||
GetDisablePathCorrectionRedirection() bool
|
GetDisablePathCorrectionRedirection() bool
|
||||||
|
// GetEnablePathIntelligence returns the Configuration.EnablePathIntelligence field.
|
||||||
|
GetEnablePathIntelligence() bool
|
||||||
// GetEnablePathEscape is the configuration.EnablePathEscape,
|
// GetEnablePathEscape is the configuration.EnablePathEscape,
|
||||||
// returns true when its escapes the path, the named parameters (if any).
|
// returns true when its escapes the path, the named parameters (if any).
|
||||||
GetEnablePathEscape() bool
|
GetEnablePathEscape() bool
|
||||||
|
|
||||||
// GetForceLowercaseRouting returns the value of the `ForceLowercaseRouting` setting.
|
// GetForceLowercaseRouting returns the value of the `ForceLowercaseRouting` setting.
|
||||||
GetForceLowercaseRouting() bool
|
GetForceLowercaseRouting() bool
|
||||||
|
// GetFireMethodNotAllowed returns the configuration.FireMethodNotAllowed.
|
||||||
|
GetFireMethodNotAllowed() bool
|
||||||
|
|
||||||
// GetEnableOptimizations returns whether
|
// GetEnableOptimizations returns whether
|
||||||
// the application has performance optimizations enabled.
|
// the application has performance optimizations enabled.
|
||||||
GetEnableOptimizations() bool
|
GetEnableOptimizations() bool
|
||||||
|
|
||||||
// GetFireMethodNotAllowed returns the configuration.FireMethodNotAllowed.
|
|
||||||
GetFireMethodNotAllowed() bool
|
|
||||||
// GetDisableBodyConsumptionOnUnmarshal returns the configuration.GetDisableBodyConsumptionOnUnmarshal,
|
// GetDisableBodyConsumptionOnUnmarshal returns the configuration.GetDisableBodyConsumptionOnUnmarshal,
|
||||||
// manages the reading behavior of the context's body readers/binders.
|
// manages the reading behavior of the context's body readers/binders.
|
||||||
// If returns true then the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`
|
// If returns true then the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`
|
||||||
|
|
|
@ -351,7 +351,7 @@ type Context interface {
|
||||||
// this request based on subdomain and request path.
|
// this request based on subdomain and request path.
|
||||||
//
|
//
|
||||||
// Order may change.
|
// 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
|
FindClosest(n int) []string
|
||||||
// IsWWW returns true if the current subdomain (if any) is www.
|
// IsWWW returns true if the current subdomain (if any) is www.
|
||||||
IsWWW() bool
|
IsWWW() bool
|
||||||
|
@ -1812,7 +1812,7 @@ func (ctx *context) Subdomain() (subdomain string) {
|
||||||
// this request based on subdomain and request path.
|
// this request based on subdomain and request path.
|
||||||
//
|
//
|
||||||
// Order may change.
|
// 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 {
|
func (ctx *context) FindClosest(n int) []string {
|
||||||
return ctx.Application().FindClosestPaths(ctx.Subdomain(), ctx.Path(), n)
|
return ctx.Application().FindClosestPaths(ctx.Subdomain(), ctx.Path(), n)
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,3 +229,19 @@ func (api *APIContainer) Any(relativePath string, handlersFn ...interface{}) (ro
|
||||||
|
|
||||||
return
|
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...)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
ctx.StatusCode(http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user