This commit is contained in:
Makis Maropoulos 2016-06-02 04:45:03 +03:00
parent 7f56cdea8c
commit 9e03a529d6
9 changed files with 288 additions and 177 deletions

View File

@ -116,7 +116,7 @@ Iris suggests you to use [this](https://github.com/gavv/httpexpect) new suite t
Versioning
------------
Current: **v3.0.0-beta**
Current: **v3.0.0-beta.1**
> Iris is an active project
@ -134,7 +134,7 @@ Todo
- [x] Create server & client side (js) library for .on('event', func action(...)) / .emit('event')... (like socket.io but supports only websocket).
- [x] Find and provide support for the most stable template engine and be able to change it via the configuration, keep html/templates support.
- [x] Extend, test and publish to the public the [Iris' cmd](https://github.com/kataras/iris/tree/master/iris).
- [x] Route naming and html url func, requested [here](https://github.com/kataras/iris/issues/165).
If you're willing to donate click [here](DONATIONS.md)

View File

@ -124,8 +124,13 @@ type (
Left string
// Right delimeter, default is }}
Right string
// Funcs for HTMLTemplate html/template
// Funcs like html/template
Funcs template.FuncMap
// Funcs like html/template
// the difference from Funcs is that these funcs
// can be used inside a layout and can override the predefined (yield,partial...) or add more custom funcs
// these can override the Funcs inside no-layout templates also, use it when you know what you're doing
LayoutFuncs template.FuncMap
}
// Pongo the configs for PongoEngine
Pongo struct {
@ -202,7 +207,7 @@ func DefaultTemplate() Template {
ContentType: "text/html",
Charset: "UTF-8",
Layout: "", // currently this is the only config which not working for pongo2 yet but I will find a way
HTMLTemplate: HTMLTemplate{Left: "{{", Right: "}}", Funcs: template.FuncMap{}},
HTMLTemplate: HTMLTemplate{Left: "{{", Right: "}}", Funcs: template.FuncMap{}, LayoutFuncs: template.FuncMap{}},
Pongo: Pongo{Filters: make(map[string]pongo2.FilterFunction, 0), Globals: make(map[string]interface{}, 0)},
Markdown: Markdown{Sanitize: false},
Amber: Amber{Funcs: template.FuncMap{}},

View File

@ -12,8 +12,11 @@ var (
ErrHandleAnnotated = errors.New("HandleAnnotated parse: %s")
// ErrControllerContextNotFound returns an error with message: 'Context *iris.Context could not be found, the Controller won't be registed.'
ErrControllerContextNotFound = errors.New("Context *iris.Context could not be found, the Controller won't be registed.")
// ErrDirectoryFileNotFound returns an errir with message: 'Directory or file %s couldn't found. Trace: +error trace'
// ErrDirectoryFileNotFound returns an error with message: 'Directory or file %s couldn't found. Trace: +error trace'
ErrDirectoryFileNotFound = errors.New("Directory or file %s couldn't found. Trace: %s")
// ErrRenderRouteNotFound returns an error with message 'Route with name +route_name not found', used inside 'url' template func
ErrRenderRouteNotFound = errors.New("Route with name %s not found")
// Plugin
// ErrPluginAlreadyExists returns an error with message: 'Cannot activate the same plugin again, plugin '+plugin name[+plugin description]' is already exists'

42
iris.go
View File

@ -1,4 +1,4 @@
// Package iris v3.0.0-beta
// Package iris v3.0.0-beta.1
//
// Note: When 'Station', we mean the Iris type.
package iris
@ -31,14 +31,13 @@ import (
const (
// Version of the iris
Version = "v3.0.0-beta"
Version = "v3.0.0-beta.1"
banner = ` _____ _
|_ _| (_)
| | ____ _ ___
| | | __|| |/ __|
_| |_| | | |\__ \
|_____|_| |_||___/
|_____|_| |_||___/ ` + Version + `
`
)
@ -118,7 +117,39 @@ func (s *Iris) newContextPool() sync.Pool {
func (s *Iris) initTemplates() {
if s.templates == nil { // because if .Templates() called before server's listen, s.templates != nil when PreListen
// init the templates
// set the custom iris-direct-integration functions, layout and no-layout if HTMLEngine is used
if s.config.Render.Template.Engine == config.HTMLEngine {
funcs := map[string]interface{}{
"url": func(routeName string, args ...interface{}) (string, error) {
r := s.RouteByName(routeName)
// check if not found
if r.GetPath() == "" {
return "", ErrRenderRouteNotFound.Format(routeName)
}
if result, ok := r.parse(args...); ok {
return result, nil
}
return "", nil
},
}
for k, v := range funcs {
// we don't want to override the user's LayoutFuncs, user should be able to override anything.
if s.config.Render.Template.HTMLTemplate.LayoutFuncs[k] == nil {
s.config.Render.Template.HTMLTemplate.LayoutFuncs[k] = v
}
if s.config.Render.Template.HTMLTemplate.Funcs[k] == nil {
s.config.Render.Template.HTMLTemplate.Funcs[k] = v
}
}
}
s.templates = template.New(s.config.Render.Template)
}
}
@ -153,7 +184,6 @@ func (s *Iris) printBanner() {
c.Add(color.FgGreen)
stationsRunning++
c.Println()
if stationsRunning > 1 {
c.Println("Server[" + strconv.Itoa(stationsRunning) + "]")
@ -163,7 +193,7 @@ func (s *Iris) printBanner() {
}
})
printTicker.Start(time.Duration(2) * time.Millisecond)
printTicker.Start(time.Duration(1) * time.Millisecond)
}

View File

@ -72,31 +72,24 @@ func Party(path string, handlersFn ...HandlerFunc) IParty {
// Handle registers a route to the server's router
// if empty method is passed then registers handler(s) for all methods, same as .Any
func Handle(method string, registedPath string, handlers ...Handler) {
DefaultIris.Handle(method, registedPath, handlers...)
func Handle(method string, registedPath string, handlers ...Handler) IRoute {
return DefaultIris.Handle(method, registedPath, handlers...)
}
// HandleFunc registers a route with a method, path string, and a handler
func HandleFunc(method string, path string, handlersFn ...HandlerFunc) {
DefaultIris.HandleFunc(method, path, handlersFn...)
}
// HandleAnnotated registers a route handler using a Struct implements iris.Handler (as anonymous property)
// which it's metadata has the form of
// `method:"path"` and returns the route and an error if any occurs
// handler is passed by func(urstruct MyStruct) Serve(ctx *Context) {}
//
// HandleAnnotated will be deprecated until the final v3 !
func HandleAnnotated(irisHandler Handler) error {
return DefaultIris.HandleAnnotated(irisHandler)
func HandleFunc(method string, path string, handlersFn ...HandlerFunc) IRoute {
return DefaultIris.HandleFunc(method, path, handlersFn...)
}
// API converts & registers a custom struct to the router
// receives three parameters
// receives two parameters
// first is the request path (string)
// second is the custom struct (interface{}) which can be anything that has a *iris.Context as field
// second is the custom struct (interface{}) which can be anything that has a *iris.Context as field.
// third are the common middlewares, is optional parameter
//
// Note that API's routes have their default-name to the full registed path,
// no need to give a special name for it, because it's not supposed to be used inside your templates.
//
// Recommend to use when you retrieve data from an external database,
// and the router-performance is not the (only) thing which slows the server's overall performance.
//
@ -173,53 +166,59 @@ func UseFunc(handlersFn ...HandlerFunc) {
}
// Get registers a route for the Get http method
func Get(path string, handlersFn ...HandlerFunc) {
DefaultIris.Get(path, handlersFn...)
func Get(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return DefaultIris.Get(path, handlersFn...)
}
// Post registers a route for the Post http method
func Post(path string, handlersFn ...HandlerFunc) {
DefaultIris.Post(path, handlersFn...)
func Post(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return DefaultIris.Post(path, handlersFn...)
}
// Put registers a route for the Put http method
func Put(path string, handlersFn ...HandlerFunc) {
DefaultIris.Put(path, handlersFn...)
func Put(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return DefaultIris.Put(path, handlersFn...)
}
// Delete registers a route for the Delete http method
func Delete(path string, handlersFn ...HandlerFunc) {
DefaultIris.Delete(path, handlersFn...)
func Delete(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return DefaultIris.Delete(path, handlersFn...)
}
// Connect registers a route for the Connect http method
func Connect(path string, handlersFn ...HandlerFunc) {
DefaultIris.Connect(path, handlersFn...)
func Connect(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return DefaultIris.Connect(path, handlersFn...)
}
// Head registers a route for the Head http method
func Head(path string, handlersFn ...HandlerFunc) {
DefaultIris.Head(path, handlersFn...)
func Head(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return DefaultIris.Head(path, handlersFn...)
}
// Options registers a route for the Options http method
func Options(path string, handlersFn ...HandlerFunc) {
DefaultIris.Options(path, handlersFn...)
func Options(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return DefaultIris.Options(path, handlersFn...)
}
// Patch registers a route for the Patch http method
func Patch(path string, handlersFn ...HandlerFunc) {
DefaultIris.Patch(path, handlersFn...)
func Patch(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return DefaultIris.Patch(path, handlersFn...)
}
// Trace registers a route for the Trace http methodd
func Trace(path string, handlersFn ...HandlerFunc) {
DefaultIris.Trace(path, handlersFn...)
func Trace(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return DefaultIris.Trace(path, handlersFn...)
}
// Any registers a route for ALL of the http methods (Get,Post,Put,Head,Patch,Options,Connect,Delete)
func Any(path string, handlersFn ...HandlerFunc) {
DefaultIris.Any(path, handlersFn...)
func Any(path string, handlersFn ...HandlerFunc) []IRoute {
return DefaultIris.Any(path, handlersFn...)
}
// RouteByName returns a route by its name,if not found then returns a route with empty path
// Note that the searching is case-sensitive
func RouteByName(lookUpName string) IRoute {
return DefaultIris.RouteByName(lookUpName)
}
// StaticHandlerFunc returns a HandlerFunc to serve static system directory

140
party.go
View File

@ -19,20 +19,19 @@ import (
type (
// IParty is the interface which implements the whole Party of routes
IParty interface {
Handle(string, string, ...Handler)
HandleFunc(string, string, ...HandlerFunc)
HandleAnnotated(Handler) error
Handle(string, string, ...Handler) IRoute
HandleFunc(string, string, ...HandlerFunc) IRoute
API(path string, controller HandlerAPI, middlewares ...HandlerFunc) error
Get(string, ...HandlerFunc)
Post(string, ...HandlerFunc)
Put(string, ...HandlerFunc)
Delete(string, ...HandlerFunc)
Connect(string, ...HandlerFunc)
Head(string, ...HandlerFunc)
Options(string, ...HandlerFunc)
Patch(string, ...HandlerFunc)
Trace(string, ...HandlerFunc)
Any(string, ...HandlerFunc)
Get(string, ...HandlerFunc) RouteNameFunc
Post(string, ...HandlerFunc) RouteNameFunc
Put(string, ...HandlerFunc) RouteNameFunc
Delete(string, ...HandlerFunc) RouteNameFunc
Connect(string, ...HandlerFunc) RouteNameFunc
Head(string, ...HandlerFunc) RouteNameFunc
Options(string, ...HandlerFunc) RouteNameFunc
Patch(string, ...HandlerFunc) RouteNameFunc
Trace(string, ...HandlerFunc) RouteNameFunc
Any(string, ...HandlerFunc) []IRoute
Use(...Handler)
UseFunc(...HandlerFunc)
StaticHandlerFunc(systemPath string, stripSlashes int, compress bool, generateIndexPages bool, indexNames []string) HandlerFunc
@ -61,13 +60,13 @@ func (p *GardenParty) IsRoot() bool {
}
// Handle registers a route to the server's router
// if empty method is passed then registers handler(s) for all methods, same as .Any
func (p *GardenParty) Handle(method string, registedPath string, handlers ...Handler) {
// if empty method is passed then registers handler(s) for all methods, same as .Any, but returns nil as result
func (p *GardenParty) Handle(method string, registedPath string, handlers ...Handler) IRoute {
if method == "" { // then use like it was .Any
for _, k := range AllMethods {
p.Handle(k, registedPath, handlers...)
}
return
return nil
}
path := fixPath(p.relativePath + registedPath) // keep the last "/" as default ex: "/xyz/"
if !p.station.config.DisablePathCorrection {
@ -79,69 +78,14 @@ func (p *GardenParty) Handle(method string, registedPath string, handlers ...Han
p.station.plugins.DoPreHandle(route)
p.station.addRoute(route)
p.station.plugins.DoPostHandle(route)
return route
}
// HandleFunc registers and returns a route with a method string, path string and a handler
// registedPath is the relative url path
// handler is the iris.Handler which you can pass anything you want via iris.ToHandlerFunc(func(res,req){})... or just use func(c *iris.Context)
func (p *GardenParty) HandleFunc(method string, registedPath string, handlersFn ...HandlerFunc) {
p.Handle(method, registedPath, ConvertToHandlers(handlersFn)...)
}
// HandleAnnotated registers a route handler using a Struct implements iris.Handler (as anonymous property)
// which it's metadata has the form of
// `method:"path"` and returns the route and an error if any occurs
// handler is passed by func(urstruct MyStruct) Serve(ctx *Context) {}
func (p *GardenParty) HandleAnnotated(irisHandler Handler) error {
var method string
var path string
var errMessage = ""
val := reflect.ValueOf(irisHandler).Elem()
for i := 0; i < val.NumField(); i++ {
typeField := val.Type().Field(i)
if typeField.Anonymous && typeField.Name == "Handler" {
tags := strings.Split(strings.TrimSpace(string(typeField.Tag)), " ")
firstTag := tags[0]
idx := strings.Index(string(firstTag), ":")
tagName := strings.ToUpper(string(firstTag[:idx]))
tagValue, unqerr := strconv.Unquote(string(firstTag[idx+1:]))
if unqerr != nil {
errMessage = errMessage + "\non getting path: " + unqerr.Error()
continue
}
path = tagValue
avalaibleMethodsStr := strings.Join(AllMethods[0:], ",")
if !strings.Contains(avalaibleMethodsStr, tagName) {
//wrong method passed
errMessage = errMessage + "\nWrong method passed to the anonymous property iris.Handler -> " + tagName
continue
}
method = tagName
} else {
errMessage = "\nStruct passed but it doesn't have an anonymous property of type iris.Hanndler, please refer to docs\n"
}
}
if errMessage == "" {
p.Handle(method, path, irisHandler)
}
var err error
if errMessage != "" {
err = ErrHandleAnnotated.Format(errMessage)
}
return err
func (p *GardenParty) HandleFunc(method string, registedPath string, handlersFn ...HandlerFunc) IRoute {
return p.Handle(method, registedPath, ConvertToHandlers(handlersFn)...)
}
// API converts & registers a custom struct to the router
@ -150,6 +94,9 @@ func (p *GardenParty) HandleAnnotated(irisHandler Handler) error {
// second is the custom struct (interface{}) which can be anything that has a *iris.Context as field.
// third are the common middlewares, is optional parameter
//
// Note that API's routes have their default-name to the full registed path,
// no need to give a special name for it, because it's not supposed to be used inside your templates.
//
// Recommend to use when you retrieve data from an external database,
// and the router-performance is not the (only) thing which slows the server's overall performance.
//
@ -300,56 +247,59 @@ func (p *GardenParty) API(path string, controller HandlerAPI, middlewares ...Han
}
// Get registers a route for the Get http method
func (p *GardenParty) Get(path string, handlersFn ...HandlerFunc) {
p.HandleFunc(MethodGet, path, handlersFn...)
func (p *GardenParty) Get(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return p.HandleFunc(MethodGet, path, handlersFn...).Name
}
// Post registers a route for the Post http method
func (p *GardenParty) Post(path string, handlersFn ...HandlerFunc) {
p.HandleFunc(MethodPost, path, handlersFn...)
func (p *GardenParty) Post(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return p.HandleFunc(MethodPost, path, handlersFn...).Name
}
// Put registers a route for the Put http method
func (p *GardenParty) Put(path string, handlersFn ...HandlerFunc) {
p.HandleFunc(MethodPut, path, handlersFn...)
func (p *GardenParty) Put(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return p.HandleFunc(MethodPut, path, handlersFn...).Name
}
// Delete registers a route for the Delete http method
func (p *GardenParty) Delete(path string, handlersFn ...HandlerFunc) {
p.HandleFunc(MethodDelete, path, handlersFn...)
func (p *GardenParty) Delete(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return p.HandleFunc(MethodDelete, path, handlersFn...).Name
}
// Connect registers a route for the Connect http method
func (p *GardenParty) Connect(path string, handlersFn ...HandlerFunc) {
p.HandleFunc(MethodConnect, path, handlersFn...)
func (p *GardenParty) Connect(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return p.HandleFunc(MethodConnect, path, handlersFn...).Name
}
// Head registers a route for the Head http method
func (p *GardenParty) Head(path string, handlersFn ...HandlerFunc) {
p.HandleFunc(MethodHead, path, handlersFn...)
func (p *GardenParty) Head(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return p.HandleFunc(MethodHead, path, handlersFn...).Name
}
// Options registers a route for the Options http method
func (p *GardenParty) Options(path string, handlersFn ...HandlerFunc) {
p.HandleFunc(MethodOptions, path, handlersFn...)
func (p *GardenParty) Options(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return p.HandleFunc(MethodOptions, path, handlersFn...).Name
}
// Patch registers a route for the Patch http method
func (p *GardenParty) Patch(path string, handlersFn ...HandlerFunc) {
p.HandleFunc(MethodPatch, path, handlersFn...)
func (p *GardenParty) Patch(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return p.HandleFunc(MethodPatch, path, handlersFn...).Name
}
// Trace registers a route for the Trace http method
func (p *GardenParty) Trace(path string, handlersFn ...HandlerFunc) {
p.HandleFunc(MethodTrace, path, handlersFn...)
func (p *GardenParty) Trace(path string, handlersFn ...HandlerFunc) RouteNameFunc {
return p.HandleFunc(MethodTrace, path, handlersFn...).Name
}
// Any registers a route for ALL of the http methods (Get,Post,Put,Head,Patch,Options,Connect,Delete)
func (p *GardenParty) Any(registedPath string, handlersFn ...HandlerFunc) {
for _, k := range AllMethods {
p.HandleFunc(k, registedPath, handlersFn...)
func (p *GardenParty) Any(registedPath string, handlersFn ...HandlerFunc) []IRoute {
theRoutes := make([]IRoute, len(AllMethods), len(AllMethods))
for idx, k := range AllMethods {
r := p.HandleFunc(k, registedPath, handlersFn...)
theRoutes[idx] = r
}
return theRoutes
}
// H_ is used to convert a context.IContext handler func to iris.HandlerFunc, is used only inside iris internal package to avoid import cycles

View File

@ -17,6 +17,9 @@ type (
Engine struct {
Config *config.Template
Templates *template.Template
// emptyFuncs returns empty functions, contains empty result for custom LayoutFuncs
emptyFuncs template.FuncMap
// Middleware
// Note:
// I see that many template engines returns html/template as result
@ -26,7 +29,10 @@ type (
}
)
var emptyFuncs = template.FuncMap{
// New creates and returns the HTMLTemplate template engine
func New(c config.Template) *Engine {
s := &Engine{Config: &c}
funcs := template.FuncMap{
"yield": func() (string, error) {
return "", fmt.Errorf("yield was called, yet no layout defined")
},
@ -38,15 +44,11 @@ var emptyFuncs = template.FuncMap{
}, "render": func() (string, error) {
return "", nil
},
// just for test with jade
/*"bold": func() (string, error) {
return "", nil
},*/
}
// New creates and returns the HTMLTemplate template engine
func New(c config.Template) *Engine {
return &Engine{Config: &c}
s.emptyFuncs = funcs
return s
}
// BuildTemplates builds the templates
@ -123,7 +125,7 @@ func (s *Engine) buildFromDir() error {
tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
}
tmpl.Funcs(emptyFuncs).Parse(contents)
tmpl.Funcs(s.emptyFuncs).Parse(contents)
break
}
}
@ -170,7 +172,7 @@ func (s *Engine) buildFromAsset() error {
tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
}
tmpl.Funcs(emptyFuncs).Parse(string(buf))
tmpl.Funcs(s.emptyFuncs).Parse(string(buf))
break
}
}
@ -207,12 +209,13 @@ func (s *Engine) layoutFuncsFor(name string, binding interface{}) {
buf, err := s.executeTemplateBuf(fullPartialName, binding)
// Return safe HTML here since we are rendering our own template.
return template.HTML(buf.String()), err
},
// just for test with jade
/*"bold": func(content string) (template.HTML, error) {
return template.HTML("<b>" + content + "</b>"), nil
},*/
}
_userLayoutFuncs := s.Config.HTMLTemplate.LayoutFuncs
if _userLayoutFuncs != nil && len(_userLayoutFuncs) > 0 {
for k, v := range _userLayoutFuncs {
funcs[k] = v
}
}
if tpl := s.Templates.Lookup(name); tpl != nil {
tpl.Funcs(funcs)

109
route.go
View File

@ -1,6 +1,8 @@
package iris
import (
"fmt"
"strconv"
"strings"
)
@ -11,17 +13,33 @@ type (
GetMethod() string
GetDomain() string
GetPath() string
GetName() string
// Name sets the name of the route
Name(string) IRoute
GetMiddleware() Middleware
HasCors() bool
// used internaly to check arguments with the route's named parameters
parse(...interface{}) (string, bool)
}
// RouteNameFunc is returned to from route handle
RouteNameFunc func(string) IRoute
// Route contains basic and temporary info about the route in order to be stored to the tree
// It's struct because we pass it ( as IRoute) to the plugins
Route struct {
method string
domain string
fullpath string
// the name of the route, the default name is just the registed path.
name string
middleware Middleware
// this is used to convert /mypath/:aparam/:something to -> /mypath/%v/%v and /mypath/* -> mypath/%v
// we use %v to escape from the conversions between strings,booleans and integers.
// used inside custom html template func 'url'
formattedPath string
// formattedParts is just the formattedPath count, used to see if we have one path parameter then the url's function arguments will be passed as one string to the %v
formattedParts int
}
)
@ -57,11 +75,43 @@ func NewRoute(method string, registedPath string, middleware Middleware) *Route
}
}
r := &Route{method: method, domain: domain, fullpath: registedPath, middleware: middleware}
r := &Route{method: method, domain: domain, fullpath: registedPath, middleware: middleware, name: registedPath, formattedPath: registedPath}
r.formatPath()
return r
}
func (r *Route) formatPath() {
// we don't care about performance here, no runtime func.
n1Len := strings.Count(r.fullpath, ":")
isMatchEverything := r.fullpath[len(r.fullpath)-1] == MatchEverythingByte
if n1Len == 0 && !isMatchEverything {
// its a static
return
}
if n1Len == 0 && isMatchEverything {
//if we have something like: /mypath/anything/* -> /mypatch/anything/%v
r.formattedPath = r.fullpath[0:len(r.fullpath)-2] + "%v"
r.formattedParts++
return
}
tempPath := r.fullpath
splittedN1 := strings.Split(r.fullpath, "/")
for _, v := range splittedN1 {
if len(v) > 0 {
if v[0] == ':' || v[0] == MatchEverythingByte {
r.formattedParts++
tempPath = strings.Replace(tempPath, v, "%v", -1) // n1Len, but let it we don't care about performance here.
}
}
}
r.formattedPath = tempPath
}
// GetMethod returns the http method
func (r Route) GetMethod() string {
return r.method
@ -77,6 +127,17 @@ func (r Route) GetPath() string {
return r.fullpath
}
// GetName returns the name of the route
func (r Route) GetName() string {
return r.name
}
// Name sets the route's name
func (r *Route) Name(newName string) IRoute {
r.name = newName
return r
}
// GetMiddleware returns the chain of the []HandlerFunc registed to this Route
func (r Route) GetMiddleware() Middleware {
return r.middleware
@ -87,6 +148,48 @@ func (r *Route) HasCors() bool {
return RouteConflicts(r, "httpmethod")
}
// used internaly to check arguments with the route's named parameters (iris.initTemplates for funcs)
func (r *Route) parse(args ...interface{}) (string, bool) {
// check if arguments are not equal to the named parameters ( : = 1, * = all named parameters split to / ), if this happens then send not found err
///TODO: I'm thinking of making an option to disable these checks and just return a result, because they have cost when rendering an html/template, not too big compared to the render action but... we will see
// can also do a check if this url can be realy served (_tree.rootBranch.GetBranch(path, ctx.Params)) and if not then return a 404 or a link to a ./templates/errors/404.html
// but we don't have access to the context itself(so we will have some memory allocations), although it's a good idea but let's keep things simple here.
argsLen := len(args)
// we have named parameters but arguments not given
if argsLen == 0 && r.formattedParts > 0 {
return "", false
}
// we have arguments but they are much more than the named parameters
// 1 check if we have /*, if yes then join all arguments to one as path and pass that as parameter
if argsLen > r.formattedParts {
if r.fullpath[len(r.fullpath)-1] == MatchEverythingByte {
// we have to convert each argument to a string in this case
argsString := make([]string, argsLen, argsLen)
for i, v := range args {
if s, ok := v.(string); ok {
argsString[i] = s
} else if num, ok := v.(int); ok {
argsString[i] = strconv.Itoa(num)
} else if b, ok := v.(bool); ok {
argsString[i] = strconv.FormatBool(b)
}
}
parameter := strings.Join(argsString, Slash)
result := fmt.Sprintf(r.formattedPath, parameter)
return result, true
}
// 2 if !1 return false
return "", false
}
return fmt.Sprintf(r.formattedPath, args...), true
}
// RouteConflicts checks for route's middleware conflicts
func RouteConflicts(r *Route, with string) bool {
for _, h := range r.middleware {

View File

@ -59,6 +59,9 @@ type router struct {
garden *Garden
methodMatch func(m1, m2 string) bool
getRequestPath func(*fasthttp.RequestCtx) []byte
// routes useful information, this info can be used to make custom links inside templates
// the route's information (can be) changed after its registration
lookups []IRoute
ServeRequest func(reqCtx *fasthttp.RequestCtx)
// errorPool is responsible to get the Context to handle not found errors
errorPool sync.Pool
@ -89,6 +92,7 @@ func newRouter(station *Iris) *router {
garden: &Garden{},
methodMatch: methodMatchFunc,
getRequestPath: getRequestPathDefault,
lookups: make([]IRoute, 0),
HTTPErrorContainer: defaultHTTPErrors(),
GardenParty: &GardenParty{relativePath: "/", station: station, root: true},
errorPool: station.newContextPool()}
@ -99,13 +103,27 @@ func newRouter(station *Iris) *router {
}
// addRoute calls the Plant, is created to set the router's station
// addRoute is a middleware between router and garden
// it just calls the garden's Plant method
// is 'thread-safe'
func (r *router) addRoute(route IRoute) {
r.mu.Lock()
defer r.mu.Unlock()
r.lookups = append(r.lookups, route)
r.garden.Plant(r.station, route)
}
// RouteByName returns a route by its name,if not found then returns a route with empty path
// Note that the searching is case-sensitive
func (r *router) RouteByName(lookUpName string) IRoute {
for _, route := range r.lookups {
if route.GetName() == lookUpName {
return route
}
}
return &Route{}
}
//check if any tree has cors setted to true, means that cors middleware is added
func (r *router) cors() (has bool) {
r.garden.visitAll(func(i int, tree *tree) {