mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 23:40:35 +01:00
Update to 8.0.2. Read HISTORY.md for the surpise
Former-commit-id: bbdf020ccaa986c332716aa7f749b7bdc24e427e
This commit is contained in:
parent
5015d92ece
commit
5752625c80
83
HISTORY.md
83
HISTORY.md
|
@ -17,6 +17,89 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
|
||||||
|
|
||||||
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`.
|
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`.
|
||||||
|
|
||||||
|
# Su, 15 July 2017 | v8.0.2
|
||||||
|
|
||||||
|
Okay my friends, this is a good time to upgrade, I did implement a feature that you were asking many times at the past.
|
||||||
|
|
||||||
|
Iris' router can now handle root-level wildcard paths `app.Get("/{paramName:path})`.
|
||||||
|
|
||||||
|
In case you're wondering: no it does not conflict with other static or dynamic routes, meaning that you can code something like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// it isn't conflicts with the rest of the static routes or dynamic routes with a path prefix.
|
||||||
|
app.Get("/{pathParamName:path}", myHandler)
|
||||||
|
```
|
||||||
|
|
||||||
|
Or even like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kataras/iris"
|
||||||
|
"github.com/kataras/iris/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := iris.New()
|
||||||
|
|
||||||
|
// this works as expected now,
|
||||||
|
// will handle all GET requests
|
||||||
|
// except:
|
||||||
|
// / -> because of app.Get("/", ...)
|
||||||
|
// /other/anything/here -> because of app.Get("/other/{paramother:path}", ...)
|
||||||
|
// /other2/anything/here -> because of app.Get("/other2/{paramothersecond:path}", ...)
|
||||||
|
// /other2/static -> because of app.Get("/other2/static", ...)
|
||||||
|
//
|
||||||
|
// It isn't conflicts with the rest of the routes, without routing performance cost!
|
||||||
|
//
|
||||||
|
// i.e /something/here/that/cannot/be/found/by/other/registered/routes/order/not/matters
|
||||||
|
app.Get("/{p:path}", h)
|
||||||
|
|
||||||
|
// this will handle only GET /
|
||||||
|
app.Get("/", staticPath)
|
||||||
|
|
||||||
|
// this will handle all GET requests starting with "/other/"
|
||||||
|
//
|
||||||
|
// i.e /other/more/than/one/path/parts
|
||||||
|
app.Get("/other/{paramother:path}", other)
|
||||||
|
|
||||||
|
// this will handle all GET requests starting with "/other2/"
|
||||||
|
// except /other2/static (because of the next static route)
|
||||||
|
//
|
||||||
|
// i.e /other2/more/than/one/path/parts
|
||||||
|
app.Get("/other2/{paramothersecond:path}", other2)
|
||||||
|
|
||||||
|
// this will handle only GET /other2/static
|
||||||
|
app.Get("/other2/static", staticPath)
|
||||||
|
|
||||||
|
app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
|
||||||
|
}
|
||||||
|
|
||||||
|
func h(ctx context.Context) {
|
||||||
|
param := ctx.Params().Get("p")
|
||||||
|
ctx.WriteString(param)
|
||||||
|
}
|
||||||
|
|
||||||
|
func other(ctx context.Context) {
|
||||||
|
param := ctx.Params().Get("paramother")
|
||||||
|
ctx.Writef("from other: %s", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
func other2(ctx context.Context) {
|
||||||
|
param := ctx.Params().Get("paramothersecond")
|
||||||
|
ctx.Writef("from other2: %s", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
func staticPath(ctx context.Context) {
|
||||||
|
ctx.Writef("from the static path: %s", ctx.Path())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you find any bugs with this change please send me a [chat message](https://kataras.rocket.chat/channel/iris) in order to investigate it, I'm totally free at weekends.
|
||||||
|
|
||||||
|
Have fun and don't forget to [star](https://github.com/kataras/iris/stargazers) the github repository, it gives me power to continue publishing my work!
|
||||||
|
|
||||||
# Th, 13 July 2017 | v8.0.1
|
# Th, 13 July 2017 | v8.0.1
|
||||||
|
|
||||||
Nothing tremendous at this minor version.
|
Nothing tremendous at this minor version.
|
||||||
|
|
|
@ -11,10 +11,7 @@ Web applications powered by Iris run everywhere, even [from an android device](h
|
||||||
[![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples)
|
[![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples)
|
||||||
[![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris)
|
[![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris)
|
||||||
|
|
||||||
<p>
|
<!--
|
||||||
<img src="https://raw.githubusercontent.com/smallnest/go-web-framework-benchmark/4db507a22c964c9bc9774c5b31afdc199a0fe8b7/benchmark.png" alt="Third-party source for transparency." />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# Mo, 10 July 2017 | v8.0.0
|
# Mo, 10 July 2017 | v8.0.0
|
||||||
|
|
||||||
### 📈 One and a half years with Iris and You...
|
### 📈 One and a half years with Iris and You...
|
||||||
|
@ -45,7 +42,7 @@ These types of projects need heart and sacrifices to continue offer the best dev
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
* [Installation](#-installation)
|
* [Installation](#-installation)
|
||||||
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#th-13-july-2017--v801)
|
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#su-15-july-2017--v802)
|
||||||
* [Learn](#-learn)
|
* [Learn](#-learn)
|
||||||
* [HTTP Listening](_examples/#http-listening)
|
* [HTTP Listening](_examples/#http-listening)
|
||||||
* [Configuration](_examples/#configuration)
|
* [Configuration](_examples/#configuration)
|
||||||
|
@ -218,7 +215,7 @@ _iris_ does not force you to use any specific ORM. With support for the most pop
|
||||||
|
|
||||||
### 📌 Version
|
### 📌 Version
|
||||||
|
|
||||||
Current: **8.0.1**
|
Current: **8.0.2**
|
||||||
|
|
||||||
Each new release is pushed to the master. It stays there until the next version. When a next version is released then the previous version goes to its own branch with `gopkg.in` as its import path (and its own vendor folder), in order to keep it working "for-ever".
|
Each new release is pushed to the master. It stays there until the next version. When a next version is released then the previous version goes to its own branch with `gopkg.in` as its import path (and its own vendor folder), in order to keep it working "for-ever".
|
||||||
|
|
||||||
|
|
|
@ -34,17 +34,54 @@ It doesn't always contain the "best ways" but it does cover each important featu
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
- [Functional](configuration/functional/main.go)
|
- [Functional](configuration/functional/main.go)
|
||||||
* [From Configuration Struct](configuration/from-configuration-structure/main.go)
|
- [From Configuration Struct](configuration/from-configuration-structure/main.go)
|
||||||
- [Import from YAML file](configuration/from-yaml-file/main.go)
|
- [Import from YAML file](configuration/from-yaml-file/main.go)
|
||||||
- [Import from TOML file](configuration/from-toml-file/main.go)
|
- [Import from TOML file](configuration/from-toml-file/main.go)
|
||||||
|
|
||||||
|
|
||||||
### Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context
|
### Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context
|
||||||
|
|
||||||
|
* `app.Get("{userid:int min(1)}", myHandler)`
|
||||||
|
* `app.Post("{asset:path}", myHandler)`
|
||||||
|
* `app.Put("{custom:string regexp([a-z]+)}", myHandler)`
|
||||||
|
|
||||||
|
Note: unlike other routers you'd seen, iris' router can handle things like these:
|
||||||
|
```go
|
||||||
|
// Matches all GET requests prefixed with "/assets/"
|
||||||
|
app.Get("/assets/{asset:path}", assetsWildcardHandler)
|
||||||
|
|
||||||
|
// Matches only GET "/"
|
||||||
|
app.Get("/", indexHandler)
|
||||||
|
// Matches only GET "/about"
|
||||||
|
app.Get("/about", aboutHandler)
|
||||||
|
|
||||||
|
// Matches all GET requests prefixed with "/profile/"
|
||||||
|
// and followed by a single path part
|
||||||
|
app.Get("/profile/{username:string}", userHandler)
|
||||||
|
// Matches only GET "/profile/me" because
|
||||||
|
// it does not conflict with /profile/{username:string}
|
||||||
|
// or the root wildcard {root:path}
|
||||||
|
app.Get("/profile/me", userHandler)
|
||||||
|
|
||||||
|
// Matches all GET requests prefixed with /users/
|
||||||
|
// and followed by a number which should be equal or bigger than 1
|
||||||
|
app.Get("/user/{userid:int min(1)}", getUserHandler)
|
||||||
|
// Matches all requests DELETE prefixed with /users/
|
||||||
|
// and following by a number which should be equal or bigger than 1
|
||||||
|
app.Delete("/user/{userid:int min(1)}", deleteUserHandler)
|
||||||
|
|
||||||
|
// Matches all GET requests except "/", "/about", anything starts with "/assets/" etc...
|
||||||
|
// because it does not conflict with the rest of the routes.
|
||||||
|
app.Get("{root:path}", rootWildcardHandler)
|
||||||
|
```
|
||||||
|
|
||||||
|
Navigate through examples for a better understanding.
|
||||||
|
|
||||||
- [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)
|
||||||
- [Dynamic Path](routing/dynamic-path/main.go)
|
- [Dynamic Path](routing/dynamic-path/main.go)
|
||||||
|
* [Root Level Wildcard Path](routing/dynamic-path/root-wildcard/main.go)
|
||||||
- [Reverse routing](routing/reverse/main.go)
|
- [Reverse routing](routing/reverse/main.go)
|
||||||
- [Custom wrapper](routing/custom-wrapper/main.go)
|
- [Custom wrapper](routing/custom-wrapper/main.go)
|
||||||
- Custom Context
|
- Custom Context
|
||||||
|
|
|
@ -28,6 +28,8 @@ func newApp() *iris.Application {
|
||||||
// })
|
// })
|
||||||
|
|
||||||
assetHandler := app.StaticHandler("./public", false, false)
|
assetHandler := app.StaticHandler("./public", false, false)
|
||||||
|
// as an alternative of SPA you can take a look at the /routing/dynamic-path/root-wildcard
|
||||||
|
// example too
|
||||||
app.SPA(assetHandler)
|
app.SPA(assetHandler)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
|
@ -25,6 +25,8 @@ func newApp() *iris.Application {
|
||||||
})
|
})
|
||||||
|
|
||||||
assetHandler := app.StaticEmbeddedHandler("./public", Asset, AssetNames)
|
assetHandler := app.StaticEmbeddedHandler("./public", Asset, AssetNames)
|
||||||
|
// as an alternative of SPA you can take a look at the /routing/dynamic-path/root-wildcard
|
||||||
|
// example too
|
||||||
app.SPA(assetHandler)
|
app.SPA(assetHandler)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
61
_examples/routing/dynamic-path/root-wildcard/main.go
Normal file
61
_examples/routing/dynamic-path/root-wildcard/main.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kataras/iris"
|
||||||
|
"github.com/kataras/iris/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := iris.New()
|
||||||
|
|
||||||
|
// this works as expected now,
|
||||||
|
// will handle all GET requests
|
||||||
|
// except:
|
||||||
|
// / -> because of app.Get("/", ...)
|
||||||
|
// /other/anything/here -> because of app.Get("/other/{paramother:path}", ...)
|
||||||
|
// /other2/anything/here -> because of app.Get("/other2/{paramothersecond:path}", ...)
|
||||||
|
// /other2/static -> because of app.Get("/other2/static", ...)
|
||||||
|
//
|
||||||
|
// It isn't conflicts with the rest of the routes, without routing performance cost!
|
||||||
|
//
|
||||||
|
// i.e /something/here/that/cannot/be/found/by/other/registered/routes/order/not/matters
|
||||||
|
app.Get("/{p:path}", h)
|
||||||
|
|
||||||
|
// this will handle only GET /
|
||||||
|
app.Get("/", staticPath)
|
||||||
|
|
||||||
|
// this will handle all GET requests starting with "/other/"
|
||||||
|
//
|
||||||
|
// i.e /other/more/than/one/path/parts
|
||||||
|
app.Get("/other/{paramother:path}", other)
|
||||||
|
|
||||||
|
// this will handle all GET requests starting with "/other2/"
|
||||||
|
// except /other2/static (because of the next static route)
|
||||||
|
//
|
||||||
|
// i.e /other2/more/than/one/path/parts
|
||||||
|
app.Get("/other2/{paramothersecond:path}", other2)
|
||||||
|
|
||||||
|
// this will handle only GET "/other2/static"
|
||||||
|
app.Get("/other2/static", staticPath)
|
||||||
|
|
||||||
|
app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
|
||||||
|
}
|
||||||
|
|
||||||
|
func h(ctx context.Context) {
|
||||||
|
param := ctx.Params().Get("p")
|
||||||
|
ctx.WriteString(param)
|
||||||
|
}
|
||||||
|
|
||||||
|
func other(ctx context.Context) {
|
||||||
|
param := ctx.Params().Get("paramother")
|
||||||
|
ctx.Writef("from other: %s", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
func other2(ctx context.Context) {
|
||||||
|
param := ctx.Params().Get("paramothersecond")
|
||||||
|
ctx.Writef("from other2: %s", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
func staticPath(ctx context.Context) {
|
||||||
|
ctx.Writef("from the static path: %s", ctx.Path())
|
||||||
|
}
|
|
@ -53,7 +53,7 @@ func (h *routerHandler) addRoute(method, subdomain, path string, handlers contex
|
||||||
t := h.getTree(method, subdomain)
|
t := h.getTree(method, subdomain)
|
||||||
|
|
||||||
if t == nil {
|
if t == nil {
|
||||||
n := make(node.Nodes, 0)
|
n := node.Nodes{}
|
||||||
// first time we register a route to this method with this subdomain
|
// first time we register a route to this method with this subdomain
|
||||||
t = &tree{Method: method, Subdomain: subdomain, Nodes: &n}
|
t = &tree{Method: method, Subdomain: subdomain, Nodes: &n}
|
||||||
h.trees = append(h.trees, t)
|
h.trees = append(h.trees, t)
|
||||||
|
@ -81,7 +81,34 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
||||||
|
|
||||||
// sort, subdomains goes first.
|
// sort, subdomains goes first.
|
||||||
sort.Slice(registeredRoutes, func(i, j int) bool {
|
sort.Slice(registeredRoutes, func(i, j int) bool {
|
||||||
return len(registeredRoutes[i].Subdomain) >= len(registeredRoutes[j].Subdomain)
|
first, second := registeredRoutes[i], registeredRoutes[j]
|
||||||
|
lsub1 := len(first.Subdomain)
|
||||||
|
lsub2 := len(second.Subdomain)
|
||||||
|
|
||||||
|
firstSlashLen := strings.Count(first.Path, "/")
|
||||||
|
secondSlashLen := strings.Count(second.Path, "/")
|
||||||
|
|
||||||
|
if lsub1 == lsub2 && first.Method == second.Method {
|
||||||
|
if secondSlashLen < firstSlashLen {
|
||||||
|
// fixes order when wildcard root is registered before other wildcard paths
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if secondSlashLen == firstSlashLen {
|
||||||
|
// fixes order when static path with the same prefix with a wildcard path
|
||||||
|
// is registered after the wildcard path, although this is managed
|
||||||
|
// by the low-level node but it couldn't work if we registered a root level wildcard, this fixes it.
|
||||||
|
if len(first.Tmpl().Params) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(second.Tmpl().Params) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the rest are handled inside the node
|
||||||
|
return lsub1 > lsub2
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
rp := errors.NewReporter()
|
rp := errors.NewReporter()
|
||||||
|
|
|
@ -15,9 +15,11 @@ type node struct {
|
||||||
s string
|
s string
|
||||||
wildcardParamName string // name of the wildcard parameter, only one per whole Node is allowed
|
wildcardParamName string // name of the wildcard parameter, only one per whole Node is allowed
|
||||||
paramNames []string // only-names
|
paramNames []string // only-names
|
||||||
children Nodes
|
childrenNodes Nodes
|
||||||
handlers context.Handlers
|
handlers context.Handlers
|
||||||
root bool
|
root bool
|
||||||
|
rootWildcard bool // if it's a wildcard {path} type on root, it should allow everything but it is not conflicts with
|
||||||
|
// any other static or dynamic or wildcard paths if exists on other nodes.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrDublicate returnned from `Add` when two or more routes have the same registered path.
|
// ErrDublicate returnned from `Add` when two or more routes have the same registered path.
|
||||||
|
@ -25,6 +27,7 @@ var ErrDublicate = errors.New("two or more routes have the same registered path"
|
||||||
|
|
||||||
// Add adds a node to the tree, returns an ErrDublicate error on failure.
|
// 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(path string, handlers context.Handlers) error {
|
||||||
|
// println("[Add] adding path: " + path)
|
||||||
// resolve params and if that node should be added as root
|
// resolve params and if that node should be added as root
|
||||||
var params []string
|
var params []string
|
||||||
var paramStart, paramEnd int
|
var paramStart, paramEnd int
|
||||||
|
@ -59,11 +62,13 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, idx := range p {
|
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(path[:idx], nil, nil, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// print("-1 nodes.Add: path: " + path + " params len: ")
|
||||||
|
// println(len(params))
|
||||||
if nidx := idx + 1; len(path) > nidx {
|
if nidx := idx + 1; len(path) > nidx {
|
||||||
if err := nodes.add(path[:nidx], nil, nil, true); err != nil {
|
if err := nodes.add(path[:nidx], nil, nil, true); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -71,6 +76,8 @@ 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(path, params, handlers, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -81,6 +88,9 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nodes *Nodes) add(path string, paramNames []string, handlers context.Handlers, root bool) (err error) {
|
func (nodes *Nodes) add(path string, paramNames []string, handlers context.Handlers, root bool) (err error) {
|
||||||
|
|
||||||
|
// println("[add] adding path: " + path)
|
||||||
|
|
||||||
// wraia etsi doulevei ara
|
// wraia etsi doulevei ara
|
||||||
// na to kanw na exei to node to diko tou wildcard parameter name
|
// na to kanw na exei to node to diko tou wildcard parameter name
|
||||||
// kai sto telos na pernei auto, me vasi to *paramname
|
// kai sto telos na pernei auto, me vasi to *paramname
|
||||||
|
@ -91,11 +101,31 @@ func (nodes *Nodes) add(path string, paramNames []string, handlers context.Handl
|
||||||
wildcardParamName := ""
|
wildcardParamName := ""
|
||||||
if wildcardIdx > 0 {
|
if wildcardIdx > 0 {
|
||||||
wildcardParamName = path[wildcardIdx+1:]
|
wildcardParamName = path[wildcardIdx+1:]
|
||||||
|
|
||||||
path = path[0:wildcardIdx-1] + "/" // replace *paramName with single slash
|
path = path[0:wildcardIdx-1] + "/" // replace *paramName with single slash
|
||||||
|
// if root wildcard, then add it as it's and return
|
||||||
|
if path == "/" {
|
||||||
|
path += "/" // if root wildcard, then do it like "//" instead of simple "/"
|
||||||
|
n := &node{
|
||||||
|
rootWildcard: true,
|
||||||
|
s: path,
|
||||||
|
wildcardParamName: wildcardParamName,
|
||||||
|
paramNames: paramNames,
|
||||||
|
handlers: handlers,
|
||||||
|
root: root,
|
||||||
|
}
|
||||||
|
*nodes = append(*nodes, n)
|
||||||
|
// println("1. nodes.Add path: " + path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
for _, n := range *nodes {
|
for _, n := range *nodes {
|
||||||
|
if n.rootWildcard {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
minlen := len(n.s)
|
minlen := len(n.s)
|
||||||
if len(path) < minlen {
|
if len(path) < minlen {
|
||||||
|
@ -112,12 +142,12 @@ loop:
|
||||||
|
|
||||||
*n = node{
|
*n = node{
|
||||||
s: n.s[:i],
|
s: n.s[:i],
|
||||||
children: Nodes{
|
childrenNodes: Nodes{
|
||||||
{
|
{
|
||||||
s: n.s[i:],
|
s: n.s[i:],
|
||||||
wildcardParamName: wildcardParamName,
|
wildcardParamName: n.wildcardParamName, // wildcardParamName
|
||||||
paramNames: n.paramNames,
|
paramNames: n.paramNames,
|
||||||
children: n.children,
|
childrenNodes: n.childrenNodes,
|
||||||
handlers: n.handlers,
|
handlers: n.handlers,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -129,20 +159,25 @@ loop:
|
||||||
},
|
},
|
||||||
root: n.root,
|
root: n.root,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("%#v\n", n)
|
||||||
|
// println("2. change n and return " + n.s[:i] + " and " + path[i:])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(path) < len(n.s) {
|
if len(path) < len(n.s) {
|
||||||
|
// println("3. change n and return | n.s[:len(path)] = " + n.s[:len(path)-1] + " and child: " + n.s[len(path)-1:])
|
||||||
|
|
||||||
*n = node{
|
*n = node{
|
||||||
s: n.s[:len(path)],
|
s: n.s[:len(path)],
|
||||||
wildcardParamName: wildcardParamName,
|
wildcardParamName: wildcardParamName,
|
||||||
paramNames: paramNames,
|
paramNames: paramNames,
|
||||||
children: Nodes{
|
childrenNodes: Nodes{
|
||||||
{
|
{
|
||||||
s: n.s[len(path):],
|
s: n.s[len(path):],
|
||||||
wildcardParamName: wildcardParamName,
|
wildcardParamName: n.wildcardParamName, // wildcardParamName
|
||||||
paramNames: n.paramNames,
|
paramNames: n.paramNames,
|
||||||
children: n.children,
|
childrenNodes: n.childrenNodes,
|
||||||
handlers: n.handlers,
|
handlers: n.handlers,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -154,13 +189,27 @@ loop:
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(path) > len(n.s) {
|
if len(path) > len(n.s) {
|
||||||
err = n.children.add(path[len(n.s):], paramNames, handlers, false)
|
if n.wildcardParamName != "" {
|
||||||
|
n := &node{
|
||||||
|
s: path,
|
||||||
|
wildcardParamName: wildcardParamName,
|
||||||
|
paramNames: paramNames,
|
||||||
|
handlers: handlers,
|
||||||
|
root: root,
|
||||||
|
}
|
||||||
|
// println("3.5. nodes.Add path: " + n.s)
|
||||||
|
*nodes = append(*nodes, n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// println("4. nodes.Add path: " + path[len(n.s):])
|
||||||
|
err = n.childrenNodes.add(path[len(n.s):], paramNames, handlers, false)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(handlers) == 0 { // missing handlers
|
if len(handlers) == 0 { // missing handlers
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(n.handlers) > 0 { // n.handlers already setted
|
if len(n.handlers) > 0 { // n.handlers already setted
|
||||||
return ErrDublicate
|
return ErrDublicate
|
||||||
}
|
}
|
||||||
|
@ -177,7 +226,7 @@ loop:
|
||||||
handlers: handlers,
|
handlers: handlers,
|
||||||
root: root,
|
root: root,
|
||||||
}
|
}
|
||||||
|
// println("5. nodes.Add path: " + n.s)
|
||||||
*nodes = append(*nodes, n)
|
*nodes = append(*nodes, n)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -190,7 +239,14 @@ func (nodes Nodes) Find(path string, params *context.RequestParams) context.Hand
|
||||||
// map the params,
|
// map the params,
|
||||||
// n.params are the param names
|
// n.params are the param names
|
||||||
if len(paramValues) > 0 {
|
if len(paramValues) > 0 {
|
||||||
|
// println("-----------")
|
||||||
|
// print("param values returned len: ")
|
||||||
|
// println(len(paramValues))
|
||||||
|
// println("first value is: " + paramValues[0])
|
||||||
|
// print("n.paramNames len: ")
|
||||||
|
// println(len(n.paramNames))
|
||||||
for i, name := range n.paramNames {
|
for i, name := range n.paramNames {
|
||||||
|
// println("setting param name: " + name + " = " + paramValues[i])
|
||||||
params.Set(name, paramValues[i])
|
params.Set(name, paramValues[i])
|
||||||
}
|
}
|
||||||
// last is the wildcard,
|
// last is the wildcard,
|
||||||
|
@ -198,7 +254,9 @@ func (nodes Nodes) Find(path string, params *context.RequestParams) context.Hand
|
||||||
// Note that n.wildcardParamName can be not empty but that doesn't meaning
|
// Note that n.wildcardParamName can be not empty but that doesn't meaning
|
||||||
// that it contains a wildcard path, so the check is required.
|
// that it contains a wildcard path, so the check is required.
|
||||||
if len(paramValues) > len(n.paramNames) {
|
if len(paramValues) > len(n.paramNames) {
|
||||||
|
// println("len(paramValues) > len(n.paramNames)")
|
||||||
lastWildcardVal := paramValues[len(paramValues)-1]
|
lastWildcardVal := paramValues[len(paramValues)-1]
|
||||||
|
// println("setting wildcard param name: " + n.wildcardParamName + " = " + lastWildcardVal)
|
||||||
params.Set(n.wildcardParamName, lastWildcardVal)
|
params.Set(n.wildcardParamName, lastWildcardVal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,6 +267,7 @@ func (nodes Nodes) Find(path string, params *context.RequestParams) context.Hand
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
|
func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
|
||||||
|
// println("request path: " + path)
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
if n.s == ":" {
|
if n.s == ":" {
|
||||||
paramEnd := strings.IndexByte(path, '/')
|
paramEnd := strings.IndexByte(path, '/')
|
||||||
|
@ -218,10 +277,26 @@ func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
|
||||||
}
|
}
|
||||||
return n, append(params, path)
|
return n, append(params, path)
|
||||||
}
|
}
|
||||||
return n.children.findChild(path[paramEnd:], append(params, path[:paramEnd]))
|
return n.childrenNodes.findChild(path[paramEnd:], append(params, path[:paramEnd]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// println("n.s: " + n.s)
|
||||||
|
// print("n.childrenNodes len: ")
|
||||||
|
// println(len(n.childrenNodes))
|
||||||
|
// print("n.root: ")
|
||||||
|
// println(n.root)
|
||||||
|
|
||||||
|
// by runtime check of:,
|
||||||
|
// if n.s == "//" && n.root && n.wildcardParamName != "" {
|
||||||
|
// but this will slow down, so we have a static field on the node itself:
|
||||||
|
if n.rootWildcard {
|
||||||
|
// println("return from n.rootWildcard")
|
||||||
|
// single root wildcard
|
||||||
|
return n, append(params, path[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(path, n.s) {
|
if !strings.HasPrefix(path, n.s) {
|
||||||
|
// fmt.Printf("---here root: %v, n.s: "+n.s+" and path: "+path+" is dynamic: %v , wildcardParamName: %s, children len: %v \n", n.root, n.isDynamic(), n.wildcardParamName, len(n.childrenNodes))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,16 +307,30 @@ func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
|
||||||
return n, params
|
return n, params
|
||||||
}
|
}
|
||||||
|
|
||||||
child, childParamNames := n.children.findChild(path[len(n.s):], params)
|
child, childParamNames := n.childrenNodes.findChild(path[len(n.s):], params)
|
||||||
|
// print("childParamNames len: ")
|
||||||
|
// println(len(childParamNames))
|
||||||
|
|
||||||
|
// if len(childParamNames) > 0 {
|
||||||
|
// println("childParamsNames[0] = " + childParamNames[0])
|
||||||
|
// }
|
||||||
|
|
||||||
if child == nil || len(child.handlers) == 0 {
|
if child == nil || len(child.handlers) == 0 {
|
||||||
|
if n.s[len(n.s)-1] == '/' && !(n.root && (n.s == "/" || len(n.childrenNodes) > 0)) {
|
||||||
if n.s[len(n.s)-1] == '/' && !(n.root && (n.s == "/" || len(n.children) > 0)) {
|
|
||||||
if len(n.handlers) == 0 {
|
if len(n.handlers) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// println("if child == nil.... | n.s = " + n.s)
|
||||||
|
// print("n.paramNames len: ")
|
||||||
|
// println(n.paramNames)
|
||||||
|
// print("n.wildcardParamName is: ")
|
||||||
|
// println(n.wildcardParamName)
|
||||||
|
// print("return n, append(params, path[len(n.s) | params: ")
|
||||||
|
// println(path[len(n.s):])
|
||||||
return n, append(params, path[len(n.s):])
|
return n, append(params, path[len(n.s):])
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +341,7 @@ func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
|
||||||
|
|
||||||
// childLen returns all the children's and their children's length.
|
// childLen returns all the children's and their children's length.
|
||||||
func (n *node) childLen() (i int) {
|
func (n *node) childLen() (i int) {
|
||||||
for _, n := range n.children {
|
for _, n := range n.childrenNodes {
|
||||||
i++
|
i++
|
||||||
i += n.childLen()
|
i += n.childLen()
|
||||||
}
|
}
|
||||||
|
@ -260,12 +349,11 @@ func (n *node) childLen() (i int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) isDynamic() bool {
|
func (n *node) isDynamic() bool {
|
||||||
return n.s == ":"
|
return n.s == ":" || n.wildcardParamName != "" || n.rootWildcard
|
||||||
}
|
}
|
||||||
|
|
||||||
// prioritize sets the static paths first.
|
// prioritize sets the static paths first.
|
||||||
func (nodes Nodes) prioritize() {
|
func (nodes Nodes) prioritize() {
|
||||||
|
|
||||||
sort.Slice(nodes, func(i, j int) bool {
|
sort.Slice(nodes, func(i, j int) bool {
|
||||||
if nodes[i].isDynamic() {
|
if nodes[i].isDynamic() {
|
||||||
return false
|
return false
|
||||||
|
@ -273,10 +361,11 @@ func (nodes Nodes) prioritize() {
|
||||||
if nodes[j].isDynamic() {
|
if nodes[j].isDynamic() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodes[i].childLen() > nodes[j].childLen()
|
return nodes[i].childLen() > nodes[j].childLen()
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
n.children.prioritize()
|
n.childrenNodes.prioritize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
179
core/router/router_wildcard_root_test.go
Normal file
179
core/router/router_wildcard_root_test.go
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
// black-box testing
|
||||||
|
//
|
||||||
|
// see _examples/routing/main_test.go for the most common router tests that you may want to see,
|
||||||
|
// this is a test for the new feature that I just coded: wildcard "/{p:path}" on root without conflicts
|
||||||
|
|
||||||
|
package router_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kataras/iris"
|
||||||
|
"github.com/kataras/iris/context"
|
||||||
|
"github.com/kataras/iris/httptest"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
same_as_request_path = "same"
|
||||||
|
from_status_code = "from"
|
||||||
|
staticPathPrefixBody = "from the static path: "
|
||||||
|
prefix_static_path_following_by_request_path = "prefix_same"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testRouteRequest struct {
|
||||||
|
method string
|
||||||
|
subdomain string
|
||||||
|
path string
|
||||||
|
expectedStatusCode int
|
||||||
|
expectedBody string
|
||||||
|
}
|
||||||
|
|
||||||
|
type testRoute struct {
|
||||||
|
method string
|
||||||
|
path string
|
||||||
|
handler context.Handler
|
||||||
|
requests []testRouteRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
var h = func(ctx context.Context) {
|
||||||
|
ctx.WriteString(ctx.Path())
|
||||||
|
}
|
||||||
|
|
||||||
|
var h2 = func(ctx context.Context) {
|
||||||
|
ctx.StatusCode(iris.StatusForbidden) // ! 200 but send the body as expected,
|
||||||
|
// we need that kind of behavior to determinate which handler is executed for routes that
|
||||||
|
// both having wildcard path but first one is registered on root level.
|
||||||
|
ctx.WriteString(ctx.Path())
|
||||||
|
}
|
||||||
|
|
||||||
|
func h3(ctx context.Context) {
|
||||||
|
ctx.Writef(staticPathPrefixBody + ctx.Path())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRouterWildcardDifferentPrefixPath(t *testing.T) {
|
||||||
|
var tt = []testRoute{
|
||||||
|
{"GET", "/s/{p:path}", h, []testRouteRequest{
|
||||||
|
{"GET", "", "/s/that/is/wildcard", iris.StatusOK, same_as_request_path},
|
||||||
|
{"GET", "", "/s/ok", iris.StatusOK, same_as_request_path},
|
||||||
|
}},
|
||||||
|
{"GET", "/som/{p:path}", h, []testRouteRequest{
|
||||||
|
{"GET", "", "/som/that/is/wildcard", iris.StatusOK, same_as_request_path},
|
||||||
|
{"GET", "", "/som/ok", iris.StatusOK, same_as_request_path},
|
||||||
|
}},
|
||||||
|
{"GET", "/some/{p:path}", h, []testRouteRequest{
|
||||||
|
{"GET", "", "/some/that/is/wildcard", iris.StatusOK, same_as_request_path},
|
||||||
|
{"GET", "", "/some1/that/is/wildcard", iris.StatusNotFound, from_status_code},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
testTheRoutes(t, tt, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRouterWildcardAndStatic(t *testing.T) {
|
||||||
|
var tt = []testRoute{
|
||||||
|
{"GET", "/some/{p:path}", h2, []testRouteRequest{
|
||||||
|
{"GET", "", "/some/that/is/wildcard", iris.StatusForbidden, same_as_request_path},
|
||||||
|
{"GET", "", "/some/did", iris.StatusForbidden, same_as_request_path},
|
||||||
|
{"GET", "", "/some1/that/is/wildcard", iris.StatusNotFound, from_status_code},
|
||||||
|
}},
|
||||||
|
{"GET", "/some/static", h, []testRouteRequest{
|
||||||
|
{"GET", "", "/some/static", iris.StatusOK, same_as_request_path},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
testTheRoutes(t, tt, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRouterWildcardRootMany(t *testing.T) {
|
||||||
|
var tt = []testRoute{
|
||||||
|
// all routes will be handlded by "h" because we added wildcard to root,
|
||||||
|
// this feature is very important and can remove noumerous of previous hacks on our apps.
|
||||||
|
{"GET", "/{p:path}", h, []testRouteRequest{
|
||||||
|
{"GET", "", "/this/is/wildcard/on/root", iris.StatusOK, same_as_request_path},
|
||||||
|
}}, // mormally, order matters, root should be registered at last
|
||||||
|
// but we change the front level order algorithm to put last these automatically
|
||||||
|
// see handler.go
|
||||||
|
{"GET", "/some/{p:path}", h2, []testRouteRequest{
|
||||||
|
{"GET", "", "/some/that/is/wildcard", iris.StatusForbidden, same_as_request_path},
|
||||||
|
{"GET", "", "/some/did", iris.StatusForbidden, same_as_request_path},
|
||||||
|
}},
|
||||||
|
{"GET", "/some/static", h, []testRouteRequest{
|
||||||
|
{"GET", "", "/some/static", iris.StatusOK, same_as_request_path},
|
||||||
|
}},
|
||||||
|
{"GET", "/some1", h, []testRouteRequest{
|
||||||
|
{"GET", "", "/some1", iris.StatusOK, same_as_request_path},
|
||||||
|
// this will show up because of the first wildcard, as we wanted to do.
|
||||||
|
{"GET", "", "/some1/that/is/wildcard", iris.StatusOK, same_as_request_path},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
testTheRoutes(t, tt, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRouterWildcardRootManyAndRootStatic(t *testing.T) {
|
||||||
|
var tt = []testRoute{
|
||||||
|
// all routes will be handlded by "h" because we added wildcard to root,
|
||||||
|
// this feature is very important and can remove noumerous of previous hacks on our apps.
|
||||||
|
{"GET", "/{p:path}", h, []testRouteRequest{
|
||||||
|
{"GET", "", "/other2almost/some", iris.StatusOK, same_as_request_path},
|
||||||
|
// it's a request to /other , not other/something, therefore the root wildcard is the handler
|
||||||
|
{"GET", "", "/other", iris.StatusOK, same_as_request_path},
|
||||||
|
}},
|
||||||
|
{"GET", "/", h, []testRouteRequest{
|
||||||
|
{"GET", "", "/", iris.StatusOK, same_as_request_path},
|
||||||
|
}},
|
||||||
|
{"GET", "/other/{paramother:path}", h2, []testRouteRequest{
|
||||||
|
{"GET", "", "/other/wildcard", iris.StatusForbidden, same_as_request_path},
|
||||||
|
{"GET", "", "/other/wildcard/here", iris.StatusForbidden, same_as_request_path},
|
||||||
|
}},
|
||||||
|
{"GET", "/other2/{paramothersecond:path}", h2, []testRouteRequest{
|
||||||
|
{"GET", "", "/other2/wildcard", iris.StatusForbidden, same_as_request_path},
|
||||||
|
{"GET", "", "/other2/more/than/one/path/parts", iris.StatusForbidden, same_as_request_path},
|
||||||
|
}},
|
||||||
|
{"GET", "/other2/static", h3, []testRouteRequest{
|
||||||
|
{"GET", "", "/other2/static", iris.StatusOK, prefix_static_path_following_by_request_path},
|
||||||
|
{"GET", "", "/other2/staticed", iris.StatusForbidden, same_as_request_path},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
testTheRoutes(t, tt, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTheRoutes(t *testing.T, tests []testRoute, debug bool) {
|
||||||
|
// build the api
|
||||||
|
app := iris.New()
|
||||||
|
for _, tt := range tests {
|
||||||
|
app.Handle(tt.method, tt.path, tt.handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup the test suite
|
||||||
|
e := httptest.New(t, app, httptest.Debug(debug))
|
||||||
|
|
||||||
|
// run the tests
|
||||||
|
for _, tt := range tests {
|
||||||
|
for _, req := range tt.requests {
|
||||||
|
method := req.method
|
||||||
|
if method == "" {
|
||||||
|
method = tt.method
|
||||||
|
}
|
||||||
|
ex := e.Request(tt.method, req.path)
|
||||||
|
if req.subdomain != "" {
|
||||||
|
ex.WithURL("http://" + req.subdomain + ".localhost:8080")
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedBody := req.expectedBody
|
||||||
|
if req.expectedBody == same_as_request_path {
|
||||||
|
expectedBody = req.path
|
||||||
|
}
|
||||||
|
if req.expectedBody == from_status_code {
|
||||||
|
expectedBody = http.StatusText(req.expectedStatusCode)
|
||||||
|
}
|
||||||
|
if req.expectedBody == prefix_static_path_following_by_request_path {
|
||||||
|
expectedBody = staticPathPrefixBody + req.path
|
||||||
|
}
|
||||||
|
|
||||||
|
ex.Expect().Status(req.expectedStatusCode).Body().Equal(expectedBody)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
doc.go
2
doc.go
|
@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
|
||||||
|
|
||||||
Current Version
|
Current Version
|
||||||
|
|
||||||
8.0.1
|
8.0.2
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ func New(t *testing.T, app *iris.Application, setters ...OptionSetter) *httpexpe
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable logger by setting it to the "Panic" level, iris never uses this
|
// disable logger by setting it to the "Panic" level, iris never uses this
|
||||||
// so it will never prints.
|
// so it will never print.
|
||||||
app.Logger().Level = 0
|
app.Logger().Level = 0
|
||||||
app.Build()
|
app.Build()
|
||||||
|
|
||||||
|
|
2
iris.go
2
iris.go
|
@ -33,7 +33,7 @@ import (
|
||||||
const (
|
const (
|
||||||
|
|
||||||
// Version is the current version number of the Iris Web Framework.
|
// Version is the current version number of the Iris Web Framework.
|
||||||
Version = "8.0.1"
|
Version = "8.0.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP status codes as registered with IANA.
|
// HTTP status codes as registered with IANA.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user