mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
Give read access to the current route, a feature that many of you asked for
Former-commit-id: 39295ac1331ee08d3047c84f5c8ea152bce96781
This commit is contained in:
parent
8b5b6b116a
commit
8b3c44b0a3
|
@ -34,6 +34,14 @@ type Application interface {
|
|||
// It is ready to use after Build state.
|
||||
ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
// GetRouteReadOnly returns the registered "read-only" route based on its name, otherwise nil.
|
||||
// One note: "routeName" should be case-sensitive. Used by the context to get the current route.
|
||||
// It returns an interface instead to reduce wrong usage and to keep the decoupled design between
|
||||
// the context and the routes.
|
||||
//
|
||||
// Look core/router/APIBuilder#GetRoute for more.
|
||||
GetRouteReadOnly(routeName string) RouteReadOnly
|
||||
|
||||
// FireErrorCode executes an error http status code handler
|
||||
// based on the context's status code.
|
||||
//
|
||||
|
|
|
@ -148,7 +148,6 @@ func (r RequestParams) Len() int {
|
|||
// context.Context is very extensible and developers can override
|
||||
// its methods if that is actually needed.
|
||||
type Context interface {
|
||||
|
||||
// BeginRequest is executing once for each request
|
||||
// it should prepare the (new or acquired from pool) context's fields for the new request.
|
||||
//
|
||||
|
@ -176,6 +175,20 @@ type Context interface {
|
|||
// Request returns the original *http.Request, as expected.
|
||||
Request() *http.Request
|
||||
|
||||
// SetCurrentRouteName sets the route's name internally,
|
||||
// in order to be able to find the correct current "read-only" Route when
|
||||
// end-developer calls the `GetCurrentRoute()` function.
|
||||
// It's being initialized by the Router, if you change that name
|
||||
// manually nothing really happens except that you'll get other
|
||||
// route via `GetCurrentRoute()`.
|
||||
// Instead, to execute a different path
|
||||
// from this context you should use the `Exec` function
|
||||
// or change the handlers via `SetHandlers/AddHandler` functions.
|
||||
SetCurrentRouteName(currentRouteName string)
|
||||
// GetCurrentRoute returns the current registered "read-only" route that
|
||||
// was being registered to this request's path.
|
||||
GetCurrentRoute() RouteReadOnly
|
||||
|
||||
// Do calls the SetHandlers(handlers)
|
||||
// and executes the first handler,
|
||||
// handlers should not be empty.
|
||||
|
@ -799,6 +812,9 @@ type context struct {
|
|||
writer ResponseWriter
|
||||
// the original http.Request
|
||||
request *http.Request
|
||||
// the current route's name registered to this request path.
|
||||
currentRouteName string
|
||||
|
||||
// the local key-value storage
|
||||
params RequestParams // url named parameters
|
||||
values memstore.Store // generic storage, middleware communication
|
||||
|
@ -880,6 +896,25 @@ func (ctx *context) Request() *http.Request {
|
|||
return ctx.request
|
||||
}
|
||||
|
||||
// SetCurrentRouteName sets the route's name internally,
|
||||
// in order to be able to find the correct current "read-only" Route when
|
||||
// end-developer calls the `GetCurrentRoute()` function.
|
||||
// It's being initialized by the Router, if you change that name
|
||||
// manually nothing really happens except that you'll get other
|
||||
// route via `GetCurrentRoute()`.
|
||||
// Instead, to execute a different path
|
||||
// from this context you should use the `Exec` function
|
||||
// or change the handlers via `SetHandlers/AddHandler` functions.
|
||||
func (ctx *context) SetCurrentRouteName(currentRouteName string) {
|
||||
ctx.currentRouteName = currentRouteName
|
||||
}
|
||||
|
||||
// GetCurrentRoute returns the current registered "read-only" route that
|
||||
// was being registered to this request's path.
|
||||
func (ctx *context) GetCurrentRoute() RouteReadOnly {
|
||||
return ctx.app.GetRouteReadOnly(ctx.currentRouteName)
|
||||
}
|
||||
|
||||
// Do calls the SetHandlers(handlers)
|
||||
// and executes the first handler,
|
||||
// handlers should not be empty.
|
||||
|
|
18
context/route.go
Normal file
18
context/route.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package context
|
||||
|
||||
// RouteReadOnly allows decoupled access to the current route
|
||||
// inside the context.
|
||||
type RouteReadOnly interface {
|
||||
// Name returns the route's name.
|
||||
Name() string
|
||||
// String returns the form of METHOD, SUBDOMAIN, TMPL PATH.
|
||||
String() string
|
||||
// Path returns the route's original registered path.
|
||||
Path() string
|
||||
|
||||
// IsOnline returns true if the route is marked as "online" (state).
|
||||
IsOnline() bool
|
||||
|
||||
// ResolvePath returns the formatted path's %v replaced with the args.
|
||||
ResolvePath(args ...string) string
|
||||
}
|
|
@ -319,6 +319,20 @@ func (api *APIBuilder) GetRoute(routeName string) *Route {
|
|||
return api.routes.get(routeName)
|
||||
}
|
||||
|
||||
// GetRouteReadOnly returns the registered "read-only" route based on its name, otherwise nil.
|
||||
// One note: "routeName" should be case-sensitive. Used by the context to get the current route.
|
||||
// It returns an interface instead to reduce wrong usage and to keep the decoupled design between
|
||||
// the context and the routes.
|
||||
//
|
||||
// Look `GetRoute` for more.
|
||||
func (api *APIBuilder) GetRouteReadOnly(routeName string) context.RouteReadOnly {
|
||||
r := api.GetRoute(routeName)
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
return routeReadOnlyWrapper{r}
|
||||
}
|
||||
|
||||
// Use appends Handler(s) to the current Party's routes and child routes.
|
||||
// If the current Party is the root, then it registers the middleware to all child Parties' routes too.
|
||||
//
|
||||
|
|
|
@ -49,7 +49,15 @@ func (h *routerHandler) getTree(method, subdomain string) *tree {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *routerHandler) addRoute(method, subdomain, path string, handlers context.Handlers) error {
|
||||
func (h *routerHandler) addRoute(r *Route) error {
|
||||
var (
|
||||
routeName = r.Name
|
||||
method = r.Method
|
||||
subdomain = r.Subdomain
|
||||
path = r.Path
|
||||
handlers = r.Handlers
|
||||
)
|
||||
|
||||
t := h.getTree(method, subdomain)
|
||||
|
||||
if t == nil {
|
||||
|
@ -58,7 +66,7 @@ func (h *routerHandler) addRoute(method, subdomain, path string, handlers contex
|
|||
t = &tree{Method: method, Subdomain: subdomain, Nodes: &n}
|
||||
h.trees = append(h.trees, t)
|
||||
}
|
||||
return t.Nodes.Add(path, handlers)
|
||||
return t.Nodes.Add(routeName, path, handlers)
|
||||
}
|
||||
|
||||
// NewDefaultHandler returns the handler which is responsible
|
||||
|
@ -125,7 +133,7 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
|||
// on route, it will be stacked shown in this build state
|
||||
// and no in the lines of the user's action, they should read
|
||||
// the docs better. Or TODO: add a link here in order to help new users.
|
||||
if err := h.addRoute(r.Method, r.Subdomain, r.Path, r.Handlers); err != nil {
|
||||
if err := h.addRoute(r); err != nil {
|
||||
// node errors:
|
||||
rp.Add("%v -> %s", err, r.String())
|
||||
}
|
||||
|
@ -202,8 +210,9 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
|
|||
continue
|
||||
}
|
||||
}
|
||||
handlers := t.Nodes.Find(path, ctx.Params())
|
||||
routeName, handlers := t.Nodes.Find(path, ctx.Params())
|
||||
if len(handlers) > 0 {
|
||||
ctx.SetCurrentRouteName(routeName)
|
||||
ctx.Do(handlers)
|
||||
// found
|
||||
return
|
||||
|
|
|
@ -13,6 +13,7 @@ type Nodes []*node
|
|||
|
||||
type node struct {
|
||||
s string
|
||||
routeName string
|
||||
wildcardParamName string // name of the wildcard parameter, only one per whole Node is allowed
|
||||
paramNames []string // only-names
|
||||
childrenNodes Nodes
|
||||
|
@ -28,7 +29,7 @@ var ErrDublicate = errors.New("two or more routes have the same registered path"
|
|||
/// TODO: clean up needed until v8.5
|
||||
|
||||
// Add adds a node to the tree, returns an ErrDublicate error on failure.
|
||||
func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
|
||||
func (nodes *Nodes) Add(routeName string, path string, handlers context.Handlers) error {
|
||||
// println("[Add] adding path: " + path)
|
||||
// resolve params and if that node should be added as root
|
||||
var params []string
|
||||
|
@ -66,13 +67,13 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
|
|||
for _, idx := range p {
|
||||
// print("-2 nodes.Add: path: " + path + " params len: ")
|
||||
// println(len(params))
|
||||
if err := nodes.add(path[:idx], nil, nil, true); err != nil {
|
||||
if err := nodes.add(routeName, path[:idx], nil, nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
// print("-1 nodes.Add: path: " + path + " params len: ")
|
||||
// println(len(params))
|
||||
if nidx := idx + 1; len(path) > nidx {
|
||||
if err := nodes.add(path[:nidx], nil, nil, true); err != nil {
|
||||
if err := nodes.add(routeName, path[:nidx], nil, nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +81,7 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
|
|||
|
||||
// print("nodes.Add: path: " + path + " params len: ")
|
||||
// println(len(params))
|
||||
if err := nodes.add(path, params, handlers, true); err != nil {
|
||||
if err := nodes.add(routeName, path, params, handlers, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -89,7 +90,7 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (nodes *Nodes) add(path string, paramNames []string, handlers context.Handlers, root bool) (err error) {
|
||||
func (nodes *Nodes) add(routeName, path string, paramNames []string, handlers context.Handlers, root bool) (err error) {
|
||||
|
||||
// println("[add] adding path: " + path)
|
||||
|
||||
|
@ -115,6 +116,7 @@ func (nodes *Nodes) add(path string, paramNames []string, handlers context.Handl
|
|||
n := &node{
|
||||
rootWildcard: rootWildcard,
|
||||
s: path,
|
||||
routeName: routeName,
|
||||
wildcardParamName: wildcardParamName,
|
||||
paramNames: paramNames,
|
||||
handlers: handlers,
|
||||
|
@ -154,6 +156,7 @@ loop:
|
|||
childrenNodes: Nodes{
|
||||
{
|
||||
s: n.s[i:],
|
||||
routeName: n.routeName,
|
||||
wildcardParamName: n.wildcardParamName, // wildcardParamName
|
||||
paramNames: n.paramNames,
|
||||
childrenNodes: n.childrenNodes,
|
||||
|
@ -161,6 +164,7 @@ loop:
|
|||
},
|
||||
{
|
||||
s: path[i:],
|
||||
routeName: routeName,
|
||||
wildcardParamName: wildcardParamName,
|
||||
paramNames: paramNames,
|
||||
handlers: handlers,
|
||||
|
@ -179,11 +183,13 @@ loop:
|
|||
|
||||
*n = node{
|
||||
s: n.s[:len(path)],
|
||||
routeName: routeName,
|
||||
wildcardParamName: wildcardParamName,
|
||||
paramNames: paramNames,
|
||||
childrenNodes: Nodes{
|
||||
{
|
||||
s: n.s[len(path):],
|
||||
routeName: n.routeName,
|
||||
wildcardParamName: n.wildcardParamName, // wildcardParamName
|
||||
paramNames: n.paramNames,
|
||||
childrenNodes: n.childrenNodes,
|
||||
|
@ -201,6 +207,7 @@ loop:
|
|||
if n.wildcardParamName != "" {
|
||||
n := &node{
|
||||
s: path,
|
||||
routeName: routeName,
|
||||
wildcardParamName: wildcardParamName,
|
||||
paramNames: paramNames,
|
||||
handlers: handlers,
|
||||
|
@ -211,7 +218,7 @@ loop:
|
|||
return
|
||||
}
|
||||
// println("4. nodes.Add path: " + path[len(n.s):])
|
||||
err = n.childrenNodes.add(path[len(n.s):], paramNames, handlers, false)
|
||||
err = n.childrenNodes.add(routeName, path[len(n.s):], paramNames, handlers, false)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -230,6 +237,7 @@ loop:
|
|||
|
||||
n := &node{
|
||||
s: path,
|
||||
routeName: routeName,
|
||||
wildcardParamName: wildcardParamName,
|
||||
paramNames: paramNames,
|
||||
handlers: handlers,
|
||||
|
@ -243,7 +251,7 @@ loop:
|
|||
|
||||
// Find resolves the path, fills its params
|
||||
// and returns the registered to the resolved node's handlers.
|
||||
func (nodes Nodes) Find(path string, params *context.RequestParams) context.Handlers {
|
||||
func (nodes Nodes) Find(path string, params *context.RequestParams) (string, context.Handlers) {
|
||||
n, paramValues := nodes.findChild(path, nil)
|
||||
if n != nil {
|
||||
// map the params,
|
||||
|
@ -270,10 +278,10 @@ func (nodes Nodes) Find(path string, params *context.RequestParams) context.Hand
|
|||
params.Set(n.wildcardParamName, lastWildcardVal)
|
||||
}
|
||||
}
|
||||
return n.handlers
|
||||
return n.routeName, n.handlers
|
||||
}
|
||||
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Exists returns true if a node with that "path" exists,
|
||||
|
|
|
@ -50,7 +50,7 @@ func NewRoute(method, subdomain, unparsedPath string,
|
|||
}
|
||||
|
||||
path = cleanPath(path) // maybe unnecessary here but who cares in this moment
|
||||
defaultName := method + subdomain + path
|
||||
defaultName := method + subdomain + tmpl.Src
|
||||
formattedPath := formatPath(path)
|
||||
|
||||
route := &Route{
|
||||
|
@ -106,7 +106,7 @@ func (r *Route) BuildHandlers() {
|
|||
} // note: no mutex needed, this should be called in-sync when server is not running of course.
|
||||
}
|
||||
|
||||
// String returns the form of METHOD, SUBDOMAIN, TMPL PATH
|
||||
// String returns the form of METHOD, SUBDOMAIN, TMPL PATH.
|
||||
func (r Route) String() string {
|
||||
return fmt.Sprintf("%s %s%s",
|
||||
r.Method, r.Subdomain, r.Tmpl().Src)
|
||||
|
@ -184,3 +184,19 @@ func (r Route) ResolvePath(args ...string) string {
|
|||
}
|
||||
return formattedPath
|
||||
}
|
||||
|
||||
type routeReadOnlyWrapper struct {
|
||||
*Route
|
||||
}
|
||||
|
||||
func (rd routeReadOnlyWrapper) Name() string {
|
||||
return rd.Route.Name
|
||||
}
|
||||
|
||||
func (rd routeReadOnlyWrapper) Subdomain() string {
|
||||
return rd.Route.Subdomain
|
||||
}
|
||||
|
||||
func (rd routeReadOnlyWrapper) Path() string {
|
||||
return rd.Route.tmpl.Src
|
||||
}
|
||||
|
|
|
@ -64,6 +64,10 @@ import (
|
|||
type Controller struct {
|
||||
// Name contains the current controller's full name.
|
||||
Name string
|
||||
|
||||
// Route is the current request context's route.
|
||||
Route context.RouteReadOnly
|
||||
|
||||
// contains the `Name` as different words, all lowercase,
|
||||
// without the "Controller" suffix if exists.
|
||||
// we need this as field because the activator
|
||||
|
@ -178,6 +182,7 @@ func (c *Controller) RelTmpl() string {
|
|||
// It's called internally.
|
||||
// End-Developer can ovverride it but it still MUST be called.
|
||||
func (c *Controller) BeginRequest(ctx context.Context) {
|
||||
c.Route = ctx.GetCurrentRoute()
|
||||
// path and path params
|
||||
c.Path = ctx.Path()
|
||||
c.Params = ctx.Params()
|
||||
|
|
Loading…
Reference in New Issue
Block a user