mirror of
https://github.com/kataras/iris.git
synced 2026-01-11 05:55:57 +00:00
Add context.FindClosest(n) to find closest paths - useful for 404 pages to suggest valid pages
Former-commit-id: 90ff7c9da5369df5bd99fbbecf9955a8c555fea5
This commit is contained in:
@@ -77,8 +77,6 @@ func NewDefaultHandler() RequestHandler {
|
||||
type RoutesProvider interface { // api builder
|
||||
GetRoutes() []*Route
|
||||
GetRoute(routeName string) *Route
|
||||
// GetStaticSites() []*StaticSite
|
||||
// Macros() *macro.Macros
|
||||
}
|
||||
|
||||
func (h *routerHandler) Build(provider RoutesProvider) error {
|
||||
|
||||
@@ -161,7 +161,7 @@ func (r *Route) BuildHandlers() {
|
||||
}
|
||||
|
||||
// String returns the form of METHOD, SUBDOMAIN, TMPL PATH.
|
||||
func (r Route) String() string {
|
||||
func (r *Route) String() string {
|
||||
return fmt.Sprintf("%s %s%s",
|
||||
r.Method, r.Subdomain, r.Tmpl().Src)
|
||||
}
|
||||
@@ -214,13 +214,13 @@ func (r *Route) SetPriority(prio float32) *Route {
|
||||
// Developer can get his registered path
|
||||
// via Tmpl().Src, Route.Path is the path
|
||||
// converted to match the underline router's specs.
|
||||
func (r Route) Tmpl() macro.Template {
|
||||
func (r *Route) Tmpl() macro.Template {
|
||||
return r.tmpl
|
||||
}
|
||||
|
||||
// RegisteredHandlersLen returns the end-developer's registered handlers, all except the macro evaluator handler
|
||||
// if was required by the build process.
|
||||
func (r Route) RegisteredHandlersLen() int {
|
||||
func (r *Route) RegisteredHandlersLen() int {
|
||||
n := len(r.Handlers)
|
||||
if handler.CanMakeHandler(r.tmpl) {
|
||||
n--
|
||||
@@ -230,7 +230,7 @@ func (r Route) RegisteredHandlersLen() int {
|
||||
}
|
||||
|
||||
// IsOnline returns true if the route is marked as "online" (state).
|
||||
func (r Route) IsOnline() bool {
|
||||
func (r *Route) IsOnline() bool {
|
||||
return r.Method != MethodNone
|
||||
}
|
||||
|
||||
@@ -270,11 +270,18 @@ func formatPath(path string) string {
|
||||
return path
|
||||
}
|
||||
|
||||
// IsStatic reports whether this route is a static route.
|
||||
// Does not contain dynamic path parameters,
|
||||
// is online and registered on GET HTTP Method.
|
||||
func (r *Route) IsStatic() bool {
|
||||
return r.IsOnline() && len(r.Tmpl().Params) == 0 && r.Method == "GET"
|
||||
}
|
||||
|
||||
// StaticPath returns the static part of the original, registered route path.
|
||||
// if /user/{id} it will return /user
|
||||
// if /user/{id}/friend/{friendid:uint64} it will return /user too
|
||||
// if /assets/{filepath:path} it will return /assets.
|
||||
func (r Route) StaticPath() string {
|
||||
func (r *Route) StaticPath() string {
|
||||
src := r.tmpl.Src
|
||||
bidx := strings.IndexByte(src, '{')
|
||||
if bidx == -1 || len(src) <= bidx {
|
||||
@@ -289,7 +296,7 @@ func (r Route) StaticPath() string {
|
||||
}
|
||||
|
||||
// ResolvePath returns the formatted path's %v replaced with the args.
|
||||
func (r Route) ResolvePath(args ...string) string {
|
||||
func (r *Route) ResolvePath(args ...string) string {
|
||||
rpath, formattedPath := r.Path, r.FormattedPath
|
||||
if rpath == formattedPath {
|
||||
// static, no need to pass args
|
||||
@@ -310,7 +317,7 @@ func (r Route) ResolvePath(args ...string) string {
|
||||
|
||||
// Trace returns some debug infos as a string sentence.
|
||||
// Should be called after Build.
|
||||
func (r Route) Trace() string {
|
||||
func (r *Route) Trace() string {
|
||||
printfmt := fmt.Sprintf("[%s:%d] %s:", r.SourceFileName, r.SourceLineNumber, r.Method)
|
||||
if r.Subdomain != "" {
|
||||
printfmt += fmt.Sprintf(" %s", r.Subdomain)
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
|
||||
"github.com/schollz/closestmatch"
|
||||
)
|
||||
|
||||
// Router is the "director".
|
||||
@@ -23,10 +25,16 @@ type Router struct {
|
||||
|
||||
cPool *context.Pool // used on RefreshRouter
|
||||
routesProvider RoutesProvider
|
||||
|
||||
// key = subdomain
|
||||
// value = closest of static routes, filled on `BuildRouter/RefreshRouter`.
|
||||
closestPaths map[string]*closestmatch.ClosestMatch
|
||||
}
|
||||
|
||||
// NewRouter returns a new empty Router.
|
||||
func NewRouter() *Router { return &Router{} }
|
||||
func NewRouter() *Router {
|
||||
return &Router{}
|
||||
}
|
||||
|
||||
// RefreshRouter re-builds the router. Should be called when a route's state
|
||||
// changed (i.e Method changed at serve-time).
|
||||
@@ -54,6 +62,28 @@ func (router *Router) AddRouteUnsafe(r *Route) error {
|
||||
return ErrNotRouteAdder
|
||||
}
|
||||
|
||||
// FindClosestPaths returns a list of "n" paths close to "path" under the given "subdomain".
|
||||
//
|
||||
// Order may change.
|
||||
func (router *Router) FindClosestPaths(subdomain, searchPath string, n int) []string {
|
||||
if router.closestPaths == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cm, ok := router.closestPaths[subdomain]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
list := cm.ClosestN(searchPath, n)
|
||||
if len(list) == 1 && list[0] == "" {
|
||||
// yes, it may return empty string as its first slice element when not found.
|
||||
return nil
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
// BuildRouter builds the router based on
|
||||
// the context factory (explicit pool in this case),
|
||||
// the request handler which manages how the main handler will multiplexes the routes
|
||||
@@ -110,6 +140,21 @@ func (router *Router) BuildRouter(cPool *context.Pool, requestHandler RequestHan
|
||||
router.mainHandler = NewWrapper(router.wrapperFunc, router.mainHandler).ServeHTTP
|
||||
}
|
||||
|
||||
// build closest.
|
||||
subdomainPaths := make(map[string][]string)
|
||||
for _, r := range router.routesProvider.GetRoutes() {
|
||||
if !r.IsStatic() {
|
||||
continue
|
||||
}
|
||||
|
||||
subdomainPaths[r.Subdomain] = append(subdomainPaths[r.Subdomain], r.Path)
|
||||
}
|
||||
|
||||
router.closestPaths = make(map[string]*closestmatch.ClosestMatch)
|
||||
for subdomain, paths := range subdomainPaths {
|
||||
router.closestPaths[subdomain] = closestmatch.New(paths, []int{3, 4, 6})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user