mirror of
https://github.com/kataras/iris.git
synced 2025-12-27 14:57:05 +00:00
20 days of unstoppable work. Waiting fo go 1.8, I didn't finish yet, some touches remains.
Former-commit-id: ed84f99c89f43fe5e980a8e6d0ee22c186f0e1b9
This commit is contained in:
332
route.go
Normal file
332
route.go
Normal file
@@ -0,0 +1,332 @@
|
||||
package iris
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// RouteInfo is just the (other idea was : RouteInfo but we needed the Change/SetName visible so...)
|
||||
// information of the registered routes.
|
||||
RouteInfo interface {
|
||||
// ChangeName & AllowOPTIONS are the only one route property
|
||||
// which can be change without any bad side-affects
|
||||
// so it's the only setter here.
|
||||
//
|
||||
// It's used on iris.Default.Handle()
|
||||
ChangeName(name string) RouteInfo
|
||||
|
||||
// Name returns the name of the route
|
||||
Name() string
|
||||
// Method returns the http method
|
||||
Method() string
|
||||
// AllowOPTIONS called when this route is targeting OPTIONS methods too
|
||||
// it's an alternative way of registring the same route with '.OPTIONS("/routepath", routeMiddleware)'
|
||||
AllowOPTIONS() RouteInfo
|
||||
// HasCors returns true if the route is targeting OPTIONS methods too
|
||||
// or it has a middleware which conflicts with "httpmethod",
|
||||
// otherwise false
|
||||
HasCors() bool
|
||||
// Subdomain returns the subdomain,if any
|
||||
Subdomain() string
|
||||
// Path returns the path
|
||||
Path() string
|
||||
// Middleware returns the slice of Handler([]Handler) registered to this route
|
||||
Middleware() Middleware
|
||||
// IsOnline returns true if the route is marked as "online" (state)
|
||||
IsOnline() bool
|
||||
}
|
||||
|
||||
// route holds useful information about route
|
||||
route struct {
|
||||
// if no name given then it's the subdomain+path
|
||||
name string
|
||||
subdomain string
|
||||
method string
|
||||
allowOptionsMethod bool
|
||||
path string
|
||||
middleware Middleware
|
||||
}
|
||||
)
|
||||
|
||||
var _ RouteInfo = &route{}
|
||||
|
||||
// RouteConflicts checks for route's middleware conflicts
|
||||
func RouteConflicts(r RouteInfo, with string) bool {
|
||||
for _, h := range r.Middleware() {
|
||||
if m, ok := h.(interface {
|
||||
Conflicts() string
|
||||
}); ok {
|
||||
if c := m.Conflicts(); c == with {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Name returns the name of the route
|
||||
func (r route) Name() string {
|
||||
return r.name
|
||||
}
|
||||
|
||||
// Name returns the name of the route
|
||||
func (r *route) ChangeName(name string) RouteInfo {
|
||||
r.name = name
|
||||
return r
|
||||
}
|
||||
|
||||
// AllowOPTIONS called when this route is targeting OPTIONS methods too
|
||||
// it's an alternative way of registring the same route with '.OPTIONS("/routepath", routeMiddleware)'
|
||||
func (r *route) AllowOPTIONS() RouteInfo {
|
||||
r.allowOptionsMethod = true
|
||||
return r
|
||||
}
|
||||
|
||||
// Method returns the http method
|
||||
func (r route) Method() string {
|
||||
return r.method
|
||||
}
|
||||
|
||||
// Subdomain returns the subdomain,if any
|
||||
func (r route) Subdomain() string {
|
||||
return r.subdomain
|
||||
}
|
||||
|
||||
// Path returns the path
|
||||
func (r route) Path() string {
|
||||
return r.path
|
||||
}
|
||||
|
||||
// Middleware returns the slice of Handler([]Handler) registered to this route
|
||||
func (r route) Middleware() Middleware {
|
||||
return r.middleware
|
||||
}
|
||||
|
||||
// IsOnline returns true if the route is marked as "online" (state)
|
||||
func (r route) IsOnline() bool {
|
||||
return r.method != MethodNone
|
||||
}
|
||||
|
||||
// HasCors returns true if the route is targeting OPTIONS methods too
|
||||
// or it has a middleware which conflicts with "httpmethod",
|
||||
// otherwise false
|
||||
func (r *route) HasCors() bool {
|
||||
return r.allowOptionsMethod || RouteConflicts(r, "httpmethod")
|
||||
}
|
||||
|
||||
// MethodChangedListener listener signature fired when route method changes
|
||||
type MethodChangedListener func(routeInfo RouteInfo, oldMethod string)
|
||||
|
||||
// RouteRepository contains the interface which is used on custom routers
|
||||
// contains methods and helpers to find a route by its name,
|
||||
// and change its method, path, middleware.
|
||||
//
|
||||
// This is not visible outside except the RouterBuilderPolicy
|
||||
type RouteRepository interface { // RouteEngine kai ContextEngine mesa sto builder adi gia RouteRepository kai ContextEngine
|
||||
RoutesInfo
|
||||
ChangeName(routeInfo RouteInfo, newName string)
|
||||
ChangeMethod(routeInfo RouteInfo, newMethod string)
|
||||
ChangePath(routeInfo RouteInfo, newPath string)
|
||||
ChangeMiddleware(routeInfo RouteInfo, newMiddleware Middleware)
|
||||
}
|
||||
|
||||
// RoutesInfo is the interface which contains the valid actions
|
||||
// permitted at RUNTIME
|
||||
type RoutesInfo interface { // RouteRepository
|
||||
Lookup(routeName string) RouteInfo
|
||||
Visit(visitor func(RouteInfo))
|
||||
OnMethodChanged(methodChangedListener MethodChangedListener)
|
||||
Online(routeInfo RouteInfo, HTTPMethod string) bool
|
||||
Offline(routeInfo RouteInfo) bool
|
||||
}
|
||||
|
||||
// routeRepository contains all the routes.
|
||||
// Implements both RouteRepository and RoutesInfo
|
||||
type routeRepository struct {
|
||||
routes []*route
|
||||
// when builded (TODO: move to its own struct)
|
||||
methodChangedListeners []MethodChangedListener
|
||||
}
|
||||
|
||||
var _ sort.Interface = &routeRepository{}
|
||||
var _ RouteRepository = &routeRepository{}
|
||||
|
||||
// Len is the number of elements in the collection.
|
||||
func (r routeRepository) Len() int {
|
||||
return len(r.routes)
|
||||
}
|
||||
|
||||
// Less reports whether the element with
|
||||
// index i should sort before the element with index j.
|
||||
func (r routeRepository) Less(i, j int) bool {
|
||||
return len(r.routes[i].Subdomain()) > len(r.routes[j].Subdomain())
|
||||
}
|
||||
|
||||
// Swap swaps the elements with indexes i and j.
|
||||
func (r routeRepository) Swap(i, j int) {
|
||||
r.routes[i], r.routes[j] = r.routes[j], r.routes[i]
|
||||
}
|
||||
|
||||
func (r *routeRepository) register(method, subdomain, path string,
|
||||
middleware Middleware) *route {
|
||||
|
||||
_route := &route{
|
||||
name: method + subdomain + path,
|
||||
method: method,
|
||||
subdomain: subdomain,
|
||||
path: path,
|
||||
middleware: middleware,
|
||||
}
|
||||
|
||||
r.routes = append(r.routes, _route)
|
||||
return _route
|
||||
}
|
||||
|
||||
func (r *routeRepository) getRouteByName(routeName string) *route {
|
||||
for i := range r.routes {
|
||||
_route := r.routes[i]
|
||||
if _route.name == routeName {
|
||||
return _route
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lookup returns a route by its name
|
||||
// used for reverse routing and templates
|
||||
func (r *routeRepository) Lookup(routeName string) RouteInfo {
|
||||
route := r.getRouteByName(routeName)
|
||||
if route == nil {
|
||||
return nil
|
||||
}
|
||||
return route
|
||||
}
|
||||
|
||||
// ChangeName changes the Name of an existing route
|
||||
func (r *routeRepository) ChangeName(routeInfo RouteInfo,
|
||||
newName string) {
|
||||
|
||||
if newName != "" {
|
||||
route := r.getRouteByName(routeInfo.Name())
|
||||
if route != nil {
|
||||
route.name = newName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *routeRepository) OnMethodChanged(methodChangedListener MethodChangedListener) {
|
||||
r.methodChangedListeners = append(r.methodChangedListeners, methodChangedListener)
|
||||
}
|
||||
|
||||
func (r *routeRepository) fireMethodChangedListeners(routeInfo RouteInfo, oldMethod string) {
|
||||
for i := 0; i < len(r.methodChangedListeners); i++ {
|
||||
r.methodChangedListeners[i](routeInfo, oldMethod)
|
||||
}
|
||||
}
|
||||
|
||||
// ChangeMethod changes the Method of an existing route
|
||||
func (r *routeRepository) ChangeMethod(routeInfo RouteInfo,
|
||||
newMethod string) {
|
||||
newMethod = strings.ToUpper(newMethod)
|
||||
valid := false
|
||||
for _, m := range AllMethods {
|
||||
if newMethod == m || newMethod == MethodNone {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
|
||||
if valid {
|
||||
route := r.getRouteByName(routeInfo.Name())
|
||||
if route != nil && route.method != newMethod {
|
||||
oldMethod := route.method
|
||||
route.method = newMethod
|
||||
r.fireMethodChangedListeners(routeInfo, oldMethod)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Online sets the state of the route to "online" with a specific http method
|
||||
// it re-builds the router
|
||||
//
|
||||
// returns true if state was actually changed
|
||||
//
|
||||
// see context.ExecRoute(routeInfo),
|
||||
// iris.Default.None(...) and iris.Routes.Online/.Routes.Offline
|
||||
// For more details look: https://github.com/kataras/iris/issues/585
|
||||
//
|
||||
// Example: https://github.com/iris-contrib/examples/tree/master/route_state
|
||||
func (r *routeRepository) Online(routeInfo RouteInfo, HTTPMethod string) bool {
|
||||
return r.changeRouteState(routeInfo, HTTPMethod)
|
||||
}
|
||||
|
||||
// Offline sets the state of the route to "offline" and re-builds the router
|
||||
//
|
||||
// returns true if state was actually changed
|
||||
//
|
||||
// see context.ExecRoute(routeInfo),
|
||||
// iris.Default.None(...) and iris.Routes.Online/.Routes.Offline
|
||||
// For more details look: https://github.com/kataras/iris/issues/585
|
||||
//
|
||||
// Example: https://github.com/iris-contrib/examples/tree/master/route_state
|
||||
func (r *routeRepository) Offline(routeInfo RouteInfo) bool {
|
||||
return r.changeRouteState(routeInfo, MethodNone)
|
||||
}
|
||||
|
||||
// changeRouteState changes the state of the route.
|
||||
// iris.MethodNone for offline
|
||||
// and iris.MethodGet/MethodPost/MethodPut/MethodDelete /MethodConnect/MethodOptions/MethodHead/MethodTrace/MethodPatch for online
|
||||
// it re-builds the router
|
||||
//
|
||||
// returns true if state was actually changed
|
||||
func (r *routeRepository) changeRouteState(routeInfo RouteInfo, HTTPMethod string) bool {
|
||||
if routeInfo != nil {
|
||||
nonSpecificMethod := len(HTTPMethod) == 0
|
||||
if routeInfo.Method() != HTTPMethod {
|
||||
if nonSpecificMethod {
|
||||
r.ChangeMethod(routeInfo, MethodGet) // if no method given, then do it for "GET" only
|
||||
} else {
|
||||
r.ChangeMethod(routeInfo, HTTPMethod)
|
||||
}
|
||||
// re-build the router/main handler should be implemented
|
||||
// on the custom router via OnMethodChanged event.
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ChangePath changes the Path of an existing route
|
||||
func (r *routeRepository) ChangePath(routeInfo RouteInfo,
|
||||
newPath string) {
|
||||
|
||||
if newPath != "" {
|
||||
route := r.getRouteByName(routeInfo.Name())
|
||||
if route != nil {
|
||||
route.path = newPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ChangeMiddleware changes the Middleware/Handlers of an existing route
|
||||
func (r *routeRepository) ChangeMiddleware(routeInfo RouteInfo,
|
||||
newMiddleware Middleware) {
|
||||
|
||||
route := r.getRouteByName(routeInfo.Name())
|
||||
if route != nil {
|
||||
route.middleware = newMiddleware
|
||||
}
|
||||
}
|
||||
|
||||
// Visit accepts a visitor func which receives a route(readonly).
|
||||
// That visitor func accepts the next route of each of the route entries.
|
||||
func (r *routeRepository) Visit(visitor func(RouteInfo)) {
|
||||
for i := range r.routes {
|
||||
visitor(r.routes[i])
|
||||
}
|
||||
}
|
||||
|
||||
// sort sorts routes by subdomain.
|
||||
func (r *routeRepository) sort() {
|
||||
sort.Sort(r)
|
||||
}
|
||||
Reference in New Issue
Block a user