diff --git a/.travis.yml b/.travis.yml
index fc1c4272..b928da0d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,8 @@ os:
- linux
- osx
go:
- - go1.9
+ - "go1.9"
+ - "go1.10"
go_import_path: github.com/kataras/iris
install:
- go get ./... # for iris-contrib/httpexpect, kataras/golog
diff --git a/Gopkg.lock b/Gopkg.lock
index 4ce68abb..ec66e816 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -107,7 +107,7 @@
branch = "master"
name = "github.com/kataras/golog"
packages = ["."]
- revision = "2ed680e7b1f34147164fa8073373e14fce02ac30"
+ revision = "dd676348ce75fa471fbbcd1bbbd131d00179756a"
[[projects]]
branch = "master"
@@ -115,12 +115,6 @@
packages = [".","terminal"]
revision = "825e39f34365e7db2c9fbc3692c16220e3bd7418"
-[[projects]]
- branch = "master"
- name = "github.com/kataras/survey"
- packages = ["."]
- revision = "20e139a6d2469769ae88e0a3579ba5df71839ca7"
-
[[projects]]
name = "github.com/klauspost/compress"
packages = ["flate","gzip"]
@@ -251,7 +245,7 @@
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
- revision = "2d6f6f883a06fc0d5f4b14a81e4c28705ea64c15"
+ revision = "c28acc882ebcbfbe8ce9f0f14b9ac26ee138dd51"
[[projects]]
branch = "master"
diff --git a/Gopkg.toml b/Gopkg.toml
index bae0c6d9..af31eeb5 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -42,10 +42,6 @@
branch = "master"
name = "github.com/kataras/golog"
-[[constraint]]
- branch = "master"
- name = "github.com/kataras/survey"
-
[[constraint]]
name = "github.com/klauspost/compress"
version = "1.2.1"
diff --git a/HISTORY.md b/HISTORY.md
index c3216d7a..5dc31053 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -17,6 +17,44 @@ 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` or let the automatic updater do that for you.
+# Sa, 10 March 2018 | v10.3.0
+
+- The only one API Change is the [Application/Context/Router#RouteExists](https://godoc.org/github.com/kataras/iris/core/router#Router.RouteExists), it accepts the `Context` as its first argument instead of last now.
+
+- Fix cors middleware via https://github.com/iris-contrib/middleware/commit/048e2be034ed172c6754448b8a54a9c55debad46, relative issue: https://github.com/kataras/iris/issues/922 (still pending for a verification).
+
+- Add `Context#NextOr` and `Context#NextOrNotFound`
+
+```go
+// NextOr checks if chain has a next handler, if so then it executes it
+// otherwise it sets a new chain assigned to this Context based on the given handler(s)
+// and executes its first handler.
+//
+// Returns true if next handler exists and executed, otherwise false.
+//
+// Note that if no next handler found and handlers are missing then
+// it sends a Status Not Found (404) to the client and it stops the execution.
+NextOr(handlers ...Handler) bool
+// NextOrNotFound checks if chain has a next handler, if so then it executes it
+// otherwise it sends a Status Not Found (404) to the client and stops the execution.
+//
+// Returns true if next handler exists and executed, otherwise false.
+NextOrNotFound() bool
+```
+
+- Add a new `Party#AllowMethods` which if called before any `Handle, Get, Post...` will clone the routes to that methods as well.
+
+- Fix trailing slash from POST method request redirection as reported at: https://github.com/kataras/iris/issues/921 via https://github.com/kataras/iris/commit/dc589d9135295b4d080a9a91e942aacbfe5d56c5
+
+- Add examples for read using custom decoder per type, read using custom decoder via `iris#UnmarshalerFunc` and to complete it add an example for the `context#ReadXML`, you can find them [here](https://github.com/kataras/iris/tree/master/_examples#how-to-read-from-contextrequest-httprequest)via https://github.com/kataras/iris/commit/78cd8e5f677fe3ff2c863c5bea7d1c161bf4c31e.
+
+- Add one more example for custom router macro functions, relative to https://github.com/kataras/iris/issues/918, you can find it [there](https://github.com/kataras/iris/blob/master/_examples/routing/dynamic-path/main.go#L144-L158), via https://github.com/kataras/iris/commit/a7690c71927cbf3aa876592fab94f04cada91b72
+
+- Add wrappers for `Pongo`'s `AsValue()` and `AsSaveValue()` by @neenar via PR: https://github.com/kataras/iris/pull/913
+
+- Remove unnecessary reflection usage on `context#UnmarshalBody` via https://github.com/kataras/iris/commit/4b9e41458b62035ea4933789c0a132c3ef2a90cc
+
+
# Th, 15 February 2018 | v10.2.1
Fix subdomains' `StaticEmbedded` & `StaticWeb` not found errors, as reported by [@speedwheel](https://github.com/speedwheel) via [facebook page's chat](https://facebook.com/iris.framework).
diff --git a/HISTORY_GR.md b/HISTORY_GR.md
index ca3e398a..538fe749 100644
--- a/HISTORY_GR.md
+++ b/HISTORY_GR.md
@@ -17,6 +17,10 @@
**Πώς να αναβαθμίσετε**: Ανοίξτε την γραμμή εντολών σας και εκτελέστε αυτήν την εντολή: `go get -u github.com/kataras/iris` ή αφήστε το αυτόματο updater να το κάνει αυτό για σας.
+# Sa, 10 March 2018 | v10.3.0
+
+This history entry is not translated yet to the Greek language yet, please refer to the original [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#sa-10-march-2018--v1030) instead.
+
# Th, 15 February 2018 | v10.2.1
Διόρθωση το οποίο αφορά 404 not found errors στα αρχεία που σερβίρονται από τα `StaticEmbedded` και `StaticWeb` των υποτομεών(subdomains), όπως αναφέρθηκε πριν λίγο από τον [@speedwheel](https://github.com/speedwheel) μέσω [της σελίδας μας στο facebook](https://facebook.com/iris.framework).
diff --git a/HISTORY_ZH.md b/HISTORY_ZH.md
index dc864ba3..781606a1 100644
--- a/HISTORY_ZH.md
+++ b/HISTORY_ZH.md
@@ -17,6 +17,10 @@
**如何升级**: 打开命令行执行以下命令: `go get -u github.com/kataras/iris` 或者等待自动更新。
+# Sa, 10 March 2018 | v10.3.0
+
+This history entry is not translated yet to the Chinese language yet, please refer to the original [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#sa-10-march-2018--v1030) instead.
+
# 2018 2月15号 | v10.2.1 版本更新
修正 子域名 (subdomain) 的 `StaticEmbedded` 和 `StaticWeb` 不存在错误, 由 [@speedwheel](https://github.com/speedwheel) 通过 [facebook page's chat](https://facebook.com/iris.framework) 反馈。
diff --git a/README.md b/README.md
index 096c1e6d..b8a0b157 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://iris-go.com/v10/recipe) [![release](https://img.shields.io/badge/release%20-v10.2-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
+[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://iris-go.com/v10/recipe) [![release](https://img.shields.io/badge/release%20-v10.3-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
Iris is a fast, simple yet fully featured and very efficient web framework for Go.
@@ -106,7 +106,7 @@ _Updated at: [Tuesday, 21 November 2017](_benchmarks/README_UNIX.md)_
## Support
-- [HISTORY](HISTORY.md#th-15-february-2018--v1021) file is your best friend, it contains information about the latest features and changes
+- [HISTORY](HISTORY.md#sa-10-march-2018--v1030) file is your best friend, it contains information about the latest features and changes
- Did you happen to find a bug? Post it at [github issues](https://github.com/kataras/iris/issues)
- Do you have any questions or need to speak with someone experienced to solve a problem at real-time? Join us to the [community chat](https://chat.iris-go.com)
- Complete our form-based user experience report by clicking [here](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)
@@ -195,7 +195,7 @@ First of all, the most correct way to begin with a web framework is to learn the
Iris has a great collection of handlers[[1]](middleware/)[[2]](https://github.com/iris-contrib/middleware) that you can use side by side with your web apps. However you are not limited to them - you are free to use any third-party middleware that is compatible with the [net/http](https://golang.org/pkg/net/http/) package, [_examples/convert-handlers](_examples/convert-handlers) will show you the way.
-Iris, unlike others, is 100% compatible with the standards and that's why the majority of the big companies that adapt Go to their workflow, like a very famous US Television Network, trust Iris; it's always up-to-date and it will be aligned with the std `net/http` package which is modernized by the Go Author on each new release of the Go Programming Language forever.
+Iris, unlike others, is 100% compatible with the standards and that's why the majority of the big companies that adapt Go to their workflow, like a very famous US Television Network, trust Iris; it's up-to-date and it will be always aligned with the std `net/http` package which is modernized by the Go Authors on each new release of the Go Programming Language.
### Articles
diff --git a/README_GR.md b/README_GR.md
index f9277217..c958d9b5 100644
--- a/README_GR.md
+++ b/README_GR.md
@@ -2,7 +2,7 @@
-[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://iris-go.com/v10/recipe) [![release](https://img.shields.io/badge/release%20-v10.2-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
+[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://iris-go.com/v10/recipe) [![release](https://img.shields.io/badge/release%20-v10.3-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
Το Iris είναι ένα γρήγορο, απλό αλλά και πλήρως λειτουργικό και πολύ αποδοτικό web framework για τη Go.
@@ -108,7 +108,7 @@ _Η τελευταία ενημέρωση έγινε την [Τρίτη, 21 Νο
## Υποστήριξη
-- To [HISTORY](HISTORY_GR.md#th-15-february-2018--v1021) αρχείο είναι ο καλύτερος σας φίλος, περιέχει πληροφορίες σχετικά με τις τελευταίες λειτουργίες(features) και αλλαγές
+- To [HISTORY](HISTORY_GR.md#sa-10-march-2018--v1030) αρχείο είναι ο καλύτερος σας φίλος, περιέχει πληροφορίες σχετικά με τις τελευταίες λειτουργίες(features) και αλλαγές
- Μήπως τυχαίνει να βρήκατε κάποιο bug; Δημοσιεύστε το στα [github issues](https://github.com/kataras/iris/issues)
- Έχετε οποιεσδήποτε ερωτήσεις ή πρέπει να μιλήσετε με κάποιον έμπειρο για την επίλυση ενός προβλήματος σε πραγματικό χρόνο; Ελάτε μαζί μας στην [συνομιλία κοινότητας](https://chat.iris-go.com)
- Συμπληρώστε την αναφορά εμπειρίας χρήστη κάνοντας κλικ [εδώ](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)
diff --git a/README_RU.md b/README_RU.md
index aaa29ad4..0d9b46d8 100644
--- a/README_RU.md
+++ b/README_RU.md
@@ -2,7 +2,7 @@
-[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://iris-go.com/v10/recipe) [![release](https://img.shields.io/badge/release%20-v10.2-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
+[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://iris-go.com/v10/recipe) [![release](https://img.shields.io/badge/release%20-v10.3-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
Iris - это быстрая, простая, но полнофункциональная и очень эффективная веб-платформа для Go.
@@ -106,7 +106,7 @@ _Обновлено: [Вторник, 21 ноября 2017 г.](_benchmarks/READ
## Поддержка
-- Файл [HISTORY](HISTORY.md#th-15-february-2018--v1021) - ваш лучший друг, он содержит информацию о последних особенностях и всех изменениях
+- Файл [HISTORY](HISTORY.md#sa-10-march-2018--v1030) - ваш лучший друг, он содержит информацию о последних особенностях и всех изменениях
- Вы случайно обнаружили ошибку? Опубликуйте ее на [Github вопросы](https://github.com/kataras/iris/issues)
- У Вас есть какие-либо вопросы или Вам нужно поговорить с кем-то, кто бы смог решить Вашу проблему в режиме реального времени? Присоединяйтесь к нам в [чате сообщества](https://chat.iris-go.com)
- Заполните наш отчет о пользовательском опыте на основе формы, нажав [здесь](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)
diff --git a/README_ZH.md b/README_ZH.md
index cebf49ae..9693820d 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -2,7 +2,7 @@
-[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://iris-go.com/v10/recipe) [![release](https://img.shields.io/badge/release%20-v10.2-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
+[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://iris-go.com/v10/recipe) [![release](https://img.shields.io/badge/release%20-v10.3-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
Iris 是一款超快、简洁高效的 Go 语言 Web开发框架。
@@ -102,7 +102,7 @@ _更新于: [2017年11月21日星期二](_benchmarks/README_UNIX.md)_
## 支持
-- [更新记录](HISTORY_ZH.md#th-15-february-2018--v1021) 是您最好的朋友,它包含有关最新功能和更改的信息
+- [更新记录](HISTORY_ZH.md#sa-10-march-2018--v1030) 是您最好的朋友,它包含有关最新功能和更改的信息
- 你碰巧找到了一个错误? 请提交 [github issues](https://github.com/kataras/iris/issues)
- 您是否有任何疑问或需要与有经验的人士交谈以实时解决问题? [加入我们的聊天](https://chat.iris-go.com)
- [点击这里完成我们基于表单的用户体验报告](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)
diff --git a/VERSION b/VERSION
index b2996f61..0f696b9f 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-10.2.1:https://github.com/kataras/iris/blob/master/HISTORY.md#th-15-february-2018--v1021
\ No newline at end of file
+10.3.0:https://github.com/kataras/iris/blob/master/HISTORY.md#sa-10-march-2018--v1030
\ No newline at end of file
diff --git a/_examples/README.md b/_examples/README.md
index 2ba8c680..a53b4998 100644
--- a/_examples/README.md
+++ b/_examples/README.md
@@ -318,8 +318,11 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her
### How to Read from `context.Request() *http.Request`
-- [Bind JSON](http_request/read-json/main.go)
-- [Bind Form](http_request/read-form/main.go)
+- [Read JSON](http_request/read-json/main.go)
+- [Read XML](http_request/read-xml/main.go)
+- [Read Form](http_request/read-form/main.go)
+- [Read Custom per type](http_request/read-custom-per-type/main.go)
+- [Read Custom via Unmarshaler](http_request/read-custom-via-unmarshaler/main.go)
- [Upload/Read File](http_request/upload-file/main.go)
- [Upload multiple files with an easy way](http_request/upload-files/main.go)
diff --git a/_examples/experimental-handlers/cors/simple/main.go b/_examples/experimental-handlers/cors/simple/main.go
index 8f9cb1bf..ea8f4e70 100644
--- a/_examples/experimental-handlers/cors/simple/main.go
+++ b/_examples/experimental-handlers/cors/simple/main.go
@@ -1,7 +1,6 @@
package main
-// $ go get github.com/rs/cors
-// $ go run main.go
+// go get -u github.com/iris-contrib/middleware/...
import (
"github.com/kataras/iris"
@@ -10,15 +9,14 @@ import (
)
func main() {
-
app := iris.New()
+
crs := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, // allows everything, use that to change the hosts.
AllowCredentials: true,
})
- v1 := app.Party("/api/v1")
- v1.Use(crs)
+ v1 := app.Party("/api/v1", crs).AllowMethods(iris.MethodOptions) // <- important for the preflight.
{
v1.Get("/home", func(ctx iris.Context) {
ctx.WriteString("Hello from /home")
@@ -29,15 +27,13 @@ func main() {
v1.Post("/send", func(ctx iris.Context) {
ctx.WriteString("sent")
})
+ v1.Put("/send", func(ctx iris.Context) {
+ ctx.WriteString("updated")
+ })
+ v1.Delete("/send", func(ctx iris.Context) {
+ ctx.WriteString("deleted")
+ })
}
- // or use that to wrap the entire router
- // even before the path and method matching
- // this should work better and with all cors' features.
- // Use that instead, if suits you.
- // app.WrapRouter(cors.WrapNext(cors.Options{
- // AllowedOrigins: []string{"*"},
- // AllowCredentials: true,
- // }))
app.Run(iris.Addr("localhost:8080"))
}
diff --git a/_examples/file-server/embedding-files-into-app/main.go b/_examples/file-server/embedding-files-into-app/main.go
index 9f5125ae..f2ec4dab 100644
--- a/_examples/file-server/embedding-files-into-app/main.go
+++ b/_examples/file-server/embedding-files-into-app/main.go
@@ -5,7 +5,7 @@ import (
)
// Follow these steps first:
-// $ go get -u github.com/jteeuwen/go-bindata/...
+// $ go get -u github.com/shuLhan/go-bindata/...
// $ go-bindata ./assets/...
// $ go build
// $ ./embedding-files-into-app
diff --git a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go b/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go
index 57b3f352..14140f92 100644
--- a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go
+++ b/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go
@@ -2,7 +2,7 @@ package main
import "github.com/kataras/iris"
-// $ go get -u github.com/jteeuwen/go-bindata/...
+// $ go get -u github.com/shuLhan/go-bindata/...
// $ go-bindata ./public/...
// $ go build
// $ ./embedded-single-page-application-with-other-routes
diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/main.go b/_examples/file-server/single-page-application/embedded-single-page-application/main.go
index fbf9618c..2f709867 100644
--- a/_examples/file-server/single-page-application/embedded-single-page-application/main.go
+++ b/_examples/file-server/single-page-application/embedded-single-page-application/main.go
@@ -4,7 +4,7 @@ import (
"github.com/kataras/iris"
)
-// $ go get -u github.com/jteeuwen/go-bindata/...
+// $ go get -u github.com/shuLhan/go-bindata/...
// $ go-bindata ./public/...
// $ go build
// $ ./embedded-single-page-application
diff --git a/_examples/http_request/read-custom-per-type/main.go b/_examples/http_request/read-custom-per-type/main.go
new file mode 100644
index 00000000..2c339988
--- /dev/null
+++ b/_examples/http_request/read-custom-per-type/main.go
@@ -0,0 +1,65 @@
+package main
+
+import (
+ "gopkg.in/yaml.v2"
+
+ "github.com/kataras/iris"
+)
+
+func main() {
+ app := newApp()
+
+ // use Postman or whatever to do a POST request
+ // (however you are always free to use app.Get and GET http method requests to read body of course)
+ // to the http://localhost:8080 with RAW BODY:
+ /*
+ addr: localhost:8080
+ serverName: Iris
+ */
+ //
+ // The response should be:
+ // Received: main.config{Addr:"localhost:8080", ServerName:"Iris"}
+ app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations)
+}
+
+func newApp() *iris.Application {
+ app := iris.New()
+ app.Post("/", handler)
+
+ return app
+}
+
+// simple yaml stuff, read more at https://github.com/go-yaml/yaml
+type config struct {
+ Addr string `yaml:"addr"`
+ ServerName string `yaml:"serverName"`
+}
+
+// Decode implements the `kataras/iris/context#BodyDecoder` optional interface
+// that any go type can implement in order to be self-decoded when reading the request's body.
+func (c *config) Decode(body []byte) error {
+ return yaml.Unmarshal(body, c)
+}
+
+func handler(ctx iris.Context) {
+ var c config
+
+ //
+ // Note:
+ // second parameter is nil because our &c implements the `context#BodyDecoder`
+ // which has a priority over the context#Unmarshaler (which can be a more global option for reading request's body)
+ // see the `http_request/read-custom-via-unmarshaler/main.go` example to learn how to use the context#Unmarshaler too.
+ //
+ // Note 2:
+ // If you need to read the body again for any reason
+ // you should disable the body consumption via `app.Run(..., iris.WithoutBodyConsumptionOnUnmarshal)`.
+ //
+
+ if err := ctx.UnmarshalBody(&c, nil); err != nil {
+ ctx.StatusCode(iris.StatusBadRequest)
+ ctx.WriteString(err.Error())
+ return
+ }
+
+ ctx.Writef("Received: %#+v", c)
+}
diff --git a/_examples/http_request/read-custom-per-type/main_test.go b/_examples/http_request/read-custom-per-type/main_test.go
new file mode 100644
index 00000000..323b08b3
--- /dev/null
+++ b/_examples/http_request/read-custom-per-type/main_test.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "testing"
+
+ "github.com/kataras/iris/httptest"
+)
+
+func TestReadCustomPerType(t *testing.T) {
+ app := newApp()
+ e := httptest.New(t, app)
+
+ expectedResponse := `Received: main.config{Addr:"localhost:8080", ServerName:"Iris"}`
+
+ e.POST("/").WithText("addr: localhost:8080\nserverName: Iris").Expect().
+ Status(httptest.StatusOK).Body().Equal(expectedResponse)
+}
diff --git a/_examples/http_request/read-custom-via-unmarshaler/main.go b/_examples/http_request/read-custom-via-unmarshaler/main.go
new file mode 100644
index 00000000..4c4bcf55
--- /dev/null
+++ b/_examples/http_request/read-custom-via-unmarshaler/main.go
@@ -0,0 +1,73 @@
+package main
+
+import (
+ "gopkg.in/yaml.v2"
+
+ "github.com/kataras/iris"
+)
+
+func main() {
+ app := newApp()
+
+ // use Postman or whatever to do a POST request
+ // (however you are always free to use app.Get and GET http method requests to read body of course)
+ // to the http://localhost:8080 with RAW BODY:
+ /*
+ addr: localhost:8080
+ serverName: Iris
+ */
+ //
+ // The response should be:
+ // Received: main.config{Addr:"localhost:8080", ServerName:"Iris"}
+ app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations)
+}
+
+func newApp() *iris.Application {
+ app := iris.New()
+ app.Post("/", handler)
+
+ return app
+}
+
+// simple yaml stuff, read more at https://github.com/go-yaml/yaml
+type config struct {
+ Addr string `yaml:"addr"`
+ ServerName string `yaml:"serverName"`
+}
+
+/*
+type myBodyDecoder struct{}
+
+var DefaultBodyDecoder = myBodyDecoder{}
+
+// Implements the `kataras/iris/context#Unmarshaler` but at our example
+// we will use the simplest `context#UnmarshalerFunc` to pass just the yaml.Unmarshal.
+//
+// Can be used as: ctx.UnmarshalBody(&c, DefaultBodyDecoder)
+func (r *myBodyDecoder) Unmarshal(data []byte, outPtr interface{}) error {
+ return yaml.Unmarshal(data, outPtr)
+}
+*/
+
+func handler(ctx iris.Context) {
+ var c config
+
+ //
+ // Note:
+ // yaml.Unmarshal already implements the `context#Unmarshaler`
+ // so we can use it directly, like the json.Unmarshal(ctx.ReadJSON), xml.Unmarshal(ctx.ReadXML)
+ // and every library which follows the best practises and is aligned with the Go standards.
+ //
+ // Note 2:
+ // If you need to read the body again for any reason
+ // you should disable the body consumption via `app.Run(..., iris.WithoutBodyConsumptionOnUnmarshal)`.
+ //
+
+ if err := ctx.UnmarshalBody(&c, iris.UnmarshalerFunc(yaml.Unmarshal)); err != nil {
+ ctx.StatusCode(iris.StatusBadRequest)
+ ctx.WriteString(err.Error())
+ return
+ }
+
+ ctx.Writef("Received: %#+v", c)
+}
diff --git a/_examples/http_request/read-custom-via-unmarshaler/main_test.go b/_examples/http_request/read-custom-via-unmarshaler/main_test.go
new file mode 100644
index 00000000..a068a65d
--- /dev/null
+++ b/_examples/http_request/read-custom-via-unmarshaler/main_test.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "testing"
+
+ "github.com/kataras/iris/httptest"
+)
+
+func TestReadCustomViaUnmarshaler(t *testing.T) {
+ app := newApp()
+ e := httptest.New(t, app)
+
+ expectedResponse := `Received: main.config{Addr:"localhost:8080", ServerName:"Iris"}`
+
+ e.POST("/").WithText("addr: localhost:8080\nserverName: Iris").Expect().
+ Status(httptest.StatusOK).Body().Equal(expectedResponse)
+}
diff --git a/_examples/http_request/read-json/main.go b/_examples/http_request/read-json/main.go
index fb4ff396..88f84407 100644
--- a/_examples/http_request/read-json/main.go
+++ b/_examples/http_request/read-json/main.go
@@ -11,16 +11,18 @@ type Company struct {
}
func MyHandler(ctx iris.Context) {
- c := &Company{}
- if err := ctx.ReadJSON(c); err != nil {
+ var c Company
+
+ if err := ctx.ReadJSON(&c); err != nil {
ctx.StatusCode(iris.StatusBadRequest)
ctx.WriteString(err.Error())
return
}
- ctx.Writef("Received: %#v\n", c)
+ ctx.Writef("Received: %#+v\n", c)
}
+// simple json stuff, read more at https://golang.org/pkg/encoding/json
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
@@ -55,9 +57,9 @@ func main() {
"Other": "Something here"
}
*/
- // and Content-Type to application/json
+ // and Content-Type to application/json (optionally but good practise)
//
// The response should be:
- // Received: &main.Company{Name:"iris-Go", City:"New York", Other:"Something here"}
- app.Run(iris.Addr(":8080"))
+ // Received: main.Company{Name:"iris-Go", City:"New York", Other:"Something here"}
+ app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations)
}
diff --git a/_examples/http_request/read-xml/main.go b/_examples/http_request/read-xml/main.go
new file mode 100644
index 00000000..5261bcfa
--- /dev/null
+++ b/_examples/http_request/read-xml/main.go
@@ -0,0 +1,50 @@
+package main
+
+import (
+ "encoding/xml"
+
+ "github.com/kataras/iris"
+)
+
+func main() {
+ app := newApp()
+
+ // use Postman or whatever to do a POST request
+ // to the http://localhost:8080 with RAW BODY:
+ /*
+
+ Description of this person, the body of this inner element.
+
+ */
+ // and Content-Type to application/xml (optionally but good practise)
+ //
+ // The response should be:
+ // Received: main.person{XMLName:xml.Name{Space:"", Local:"person"}, Name:"Winston Churchill", Age:90, Description:"Description of this person, the body of this inner element."}
+ app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations)
+}
+
+func newApp() *iris.Application {
+ app := iris.New()
+ app.Post("/", handler)
+
+ return app
+}
+
+// simple xml stuff, read more at https://golang.org/pkg/encoding/xml
+type person struct {
+ XMLName xml.Name `xml:"person"` // element name
+ Name string `xml:"name,attr"` // ,attr for attribute.
+ Age int `xml:"age,attr"` // ,attr attribute.
+ Description string `xml:"description"` // inner element name, value is its body.
+}
+
+func handler(ctx iris.Context) {
+ var p person
+ if err := ctx.ReadXML(&p); err != nil {
+ ctx.StatusCode(iris.StatusBadRequest)
+ ctx.WriteString(err.Error())
+ return
+ }
+
+ ctx.Writef("Received: %#+v", p)
+}
diff --git a/_examples/http_request/read-xml/main_test.go b/_examples/http_request/read-xml/main_test.go
new file mode 100644
index 00000000..da4fbd7a
--- /dev/null
+++ b/_examples/http_request/read-xml/main_test.go
@@ -0,0 +1,18 @@
+package main
+
+import (
+ "testing"
+
+ "github.com/kataras/iris/httptest"
+)
+
+func TestReadXML(t *testing.T) {
+ app := newApp()
+ e := httptest.New(t, app)
+
+ expectedResponse := `Received: main.person{XMLName:xml.Name{Space:"", Local:"person"}, Name:"Winston Churchill", Age:90, Description:"Description of this person, the body of this inner element."}`
+ send := `Description of this person, the body of this inner element.`
+
+ e.POST("/").WithText(send).Expect().
+ Status(httptest.StatusOK).Body().Equal(expectedResponse)
+}
diff --git a/_examples/http_responsewriter/herotemplate/app.go b/_examples/http_responsewriter/herotemplate/app.go
index 4f1412ac..f948f71d 100644
--- a/_examples/http_responsewriter/herotemplate/app.go
+++ b/_examples/http_responsewriter/herotemplate/app.go
@@ -2,7 +2,6 @@ package main
import (
"bytes"
- "log"
"github.com/kataras/iris/_examples/http_responsewriter/herotemplate/template"
@@ -13,11 +12,15 @@ import (
// $ go run app.go
//
// Read more at https://github.com/shiyanhui/hero/hero
+
func main() {
app := iris.New()
app.Get("/users", func(ctx iris.Context) {
+ ctx.Gzip(true)
+ ctx.ContentType("text/html")
+
var userList = []string{
"Alice",
"Bob",
@@ -25,30 +28,27 @@ func main() {
}
// Had better use buffer sync.Pool.
- // Hero exports GetBuffer and PutBuffer for this.
+ // Hero(github.com/shiyanhui/hero/hero) exports GetBuffer and PutBuffer for this.
//
// buffer := hero.GetBuffer()
// defer hero.PutBuffer(buffer)
- buffer := new(bytes.Buffer)
- template.UserList(userList, buffer)
-
- if _, err := ctx.Write(buffer.Bytes()); err != nil {
- log.Printf("ERR: %s\n", err)
- }
- })
-
- app.Get("/users2", func(ctx iris.Context) {
- var userList = []string{
- "Alice",
- "Bob",
- "Tom",
- }
+ // buffer := new(bytes.Buffer)
+ // template.UserList(userList, buffer)
+ // ctx.Write(buffer.Bytes())
// using an io.Writer for automatic buffer management (i.e. hero built-in buffer pool),
// iris context implements the io.Writer by its ResponseWriter
// which is an enhanced version of the standard http.ResponseWriter
- // but still 100% compatible.
- template.UserListToWriter(userList, ctx)
+ // but still 100% compatible, GzipResponseWriter too:
+ // _, err := template.UserListToWriter(userList, ctx.GzipResponseWriter())
+ buffer := new(bytes.Buffer)
+ template.UserList(userList, buffer)
+
+ _, err := ctx.Write(buffer.Bytes())
+ if err != nil {
+ ctx.StatusCode(iris.StatusInternalServerError)
+ ctx.WriteString(err.Error())
+ }
})
app.Run(iris.Addr(":8080"))
diff --git a/_examples/routing/dynamic-path/main.go b/_examples/routing/dynamic-path/main.go
index b7e78c40..5a3f0b57 100644
--- a/_examples/routing/dynamic-path/main.go
+++ b/_examples/routing/dynamic-path/main.go
@@ -1,6 +1,7 @@
package main
import (
+ "regexp"
"strconv"
"github.com/kataras/iris"
@@ -140,6 +141,24 @@ func main() {
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
}) // this will throw e 504 error code instead of 404 if all route's macros not passed.
+ // Another example using a custom regexp and any custom logic.
+ latLonExpr := "^-?[0-9]{1,3}(?:\\.[0-9]{1,10})?$"
+ latLonRegex, err := regexp.Compile(latLonExpr)
+ if err != nil {
+ panic(err)
+ }
+
+ app.Macros().String.RegisterFunc("coordinate", func() func(paramName string) (ok bool) {
+ // MatchString is a type of func(string) bool, so we can return that as it's.
+ return latLonRegex.MatchString
+ })
+
+ app.Get("/coordinates/{lat:string coordinate() else 502}/{lon:string coordinate() else 502}", func(ctx iris.Context) {
+ ctx.Writef("Lat: %s | Lon: %s", ctx.Params().Get("lat"), ctx.Params().Get("lon"))
+ })
+
+ //
+
// http://localhost:8080/game/a-zA-Z/level/0-9
// remember, alphabetical is lowercase or uppercase letters only.
app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) {
diff --git a/_examples/routing/route-state/main.go b/_examples/routing/route-state/main.go
index f85426f8..ec4f03db 100644
--- a/_examples/routing/route-state/main.go
+++ b/_examples/routing/route-state/main.go
@@ -8,7 +8,7 @@ func main() {
app := iris.New()
none := app.None("/invisible/{username}", func(ctx iris.Context) {
- ctx.Writef("Hello %s with method: %s", ctx.Values().GetString("username"), ctx.Method())
+ ctx.Writef("Hello %s with method: %s", ctx.Params().Get("username"), ctx.Method())
if from := ctx.Values().GetString("from"); from != "" {
ctx.Writef("\nI see that you're coming from %s", from)
diff --git a/_examples/tutorial/online-visitors/main.go b/_examples/tutorial/online-visitors/main.go
index 023bda63..e6f3fa11 100644
--- a/_examples/tutorial/online-visitors/main.go
+++ b/_examples/tutorial/online-visitors/main.go
@@ -58,15 +58,11 @@ func (v *pageView) increment() {
}
func (v *pageView) decrement() {
- oldCount := v.count
- if oldCount > 0 {
- atomic.StoreUint64(&v.count, oldCount-1)
- }
+ atomic.AddUint64(&v.count, ^uint64(0))
}
func (v *pageView) getCount() uint64 {
- val := atomic.LoadUint64(&v.count)
- return val
+ return atomic.LoadUint64(&v.count)
}
type (
diff --git a/_examples/view/embedding-templates-into-app/main.go b/_examples/view/embedding-templates-into-app/main.go
index 60e73940..69372f95 100644
--- a/_examples/view/embedding-templates-into-app/main.go
+++ b/_examples/view/embedding-templates-into-app/main.go
@@ -13,7 +13,7 @@ func main() {
return "Greetings " + s + "!"
})
- // $ go get -u github.com/jteeuwen/go-bindata/...
+ // $ go get -u github.com/shuLhan/go-bindata/...
// $ go-bindata ./templates/...
// $ go build
// $ ./embedding-templates-into-app
diff --git a/configuration_test.go b/configuration_test.go
index f5aed4f7..61915011 100644
--- a/configuration_test.go
+++ b/configuration_test.go
@@ -30,7 +30,7 @@ func TestConfigurationStatic(t *testing.T) {
afterNew = *app.config
- if app.config.DisableBodyConsumptionOnUnmarshal == false {
+ if !app.config.DisableBodyConsumptionOnUnmarshal {
t.Fatalf("Passing a Configuration field as Option fails, expected DisableBodyConsumptionOnUnmarshal to be true but was false")
}
diff --git a/context/application.go b/context/application.go
index 90738ce7..9a94fbcb 100644
--- a/context/application.go
+++ b/context/application.go
@@ -48,4 +48,8 @@ type Application interface {
// If a handler is not already registered,
// then it creates & registers a new trivial handler on the-fly.
FireErrorCode(ctx Context)
+
+ // RouteExists reports whether a particular route exists
+ // It will search from the current subdomain of context's host, if not inside the root domain.
+ RouteExists(ctx Context, method, path string) bool
}
diff --git a/context/context.go b/context/context.go
index dc3ecf80..040258ee 100644
--- a/context/context.go
+++ b/context/context.go
@@ -15,7 +15,6 @@ import (
"os"
"path"
"path/filepath"
- "reflect"
"regexp"
"strconv"
"strings"
@@ -49,20 +48,24 @@ type (
//
// Note: This is totally optionally, the default decoders
// for ReadJSON is the encoding/json and for ReadXML is the encoding/xml.
+ //
+ // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-per-type/main.go
BodyDecoder interface {
Decode(data []byte) error
}
- // Unmarshaler is the interface implemented by types that can unmarshal any raw data
- // TIP INFO: Any v object which implements the BodyDecoder can be override the unmarshaler.
+ // Unmarshaler is the interface implemented by types that can unmarshal any raw data.
+ // TIP INFO: Any pointer to a value which implements the BodyDecoder can be override the unmarshaler.
Unmarshaler interface {
- Unmarshal(data []byte, v interface{}) error
+ Unmarshal(data []byte, outPtr interface{}) error
}
// UnmarshalerFunc a shortcut for the Unmarshaler interface
//
// See 'Unmarshaler' and 'BodyDecoder' for more.
- UnmarshalerFunc func(data []byte, v interface{}) error
+ //
+ // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go
+ UnmarshalerFunc func(data []byte, outPtr interface{}) error
)
// Unmarshal parses the X-encoded data and stores the result in the value pointed to by v.
@@ -310,7 +313,21 @@ type Context interface {
//
// Note: Custom context should override this method in order to be able to pass its own context.Context implementation.
Next()
- // NextHandler returns(but it is NOT executes) the next handler from the handlers chain.
+ // NextOr checks if chain has a next handler, if so then it executes it
+ // otherwise it sets a new chain assigned to this Context based on the given handler(s)
+ // and executes its first handler.
+ //
+ // Returns true if next handler exists and executed, otherwise false.
+ //
+ // Note that if no next handler found and handlers are missing then
+ // it sends a Status Not Found (404) to the client and it stops the execution.
+ NextOr(handlers ...Handler) bool
+ // NextOrNotFound checks if chain has a next handler, if so then it executes it
+ // otherwise it sends a Status Not Found (404) to the client and stops the execution.
+ //
+ // Returns true if next handler exists and executed, otherwise false.
+ NextOrNotFound() bool
+ // NextHandler returns (it doesn't execute) the next handler from the handlers chain.
//
// Use .Skip() to skip this handler if needed to execute the next of this returning handler.
NextHandler() Handler
@@ -550,6 +567,8 @@ type Context interface {
//
// The default form's memory maximum size is 32MB, it can be changed by the
// `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
+ //
+ // Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-file
FormFile(key string) (multipart.File, *multipart.FileHeader, error)
// UploadFormFiles uploads any received file(s) from the client
// to the system physical location "destDirectory".
@@ -574,6 +593,9 @@ type Context interface {
// `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
//
// See `FormFile` to a more controlled to receive a file.
+ //
+ //
+ // Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-files
UploadFormFiles(destDirectory string, before ...func(Context, *multipart.FileHeader)) (n int64, err error)
// +------------------------------------------------------------+
@@ -596,16 +618,24 @@ type Context interface {
// should be called before reading the request body from the client.
SetMaxRequestBodySize(limitOverBytes int64)
- // UnmarshalBody reads the request's body and binds it to a value or pointer of any type
+ // UnmarshalBody reads the request's body and binds it to a value or pointer of any type.
// Examples of usage: context.ReadJSON, context.ReadXML.
- UnmarshalBody(v interface{}, unmarshaler Unmarshaler) error
- // ReadJSON reads JSON from request's body and binds it to a value of any json-valid type.
- ReadJSON(jsonObject interface{}) error
- // ReadXML reads XML from request's body and binds it to a value of any xml-valid type.
- ReadXML(xmlObject interface{}) error
+ //
+ // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go
+ UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) error
+ // ReadJSON reads JSON from request's body and binds it to a pointer of a value of any json-valid type.
+ //
+ // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-json/main.go
+ ReadJSON(jsonObjectPtr interface{}) error
+ // ReadXML reads XML from request's body and binds it to a pointer of a value of any xml-valid type.
+ //
+ // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-xml/main.go
+ ReadXML(xmlObjectPtr interface{}) error
// ReadForm binds the formObject with the form data
// it supports any kind of struct.
- ReadForm(formObject interface{}) error
+ //
+ // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-form/main.go
+ ReadForm(formObjectPtr interface{}) error
// +------------------------------------------------------------+
// | Body (raw) Writers |
@@ -880,7 +910,7 @@ type Context interface {
// TransactionsSkipped returns true if the transactions skipped or canceled at all.
TransactionsSkipped() bool
- // Exec calls the framewrok's ServeCtx
+ // Exec calls the `context/Application#ServeCtx`
// based on this context but with a changed method and path
// like it was requested by the user, but it is not.
//
@@ -903,7 +933,11 @@ type Context interface {
// Context's Values and the Session are kept in order to be able to communicate via the result route.
//
// It's for extreme use cases, 99% of the times will never be useful for you.
- Exec(method string, path string)
+ Exec(method, path string)
+
+ // RouteExists reports whether a particular route exists
+ // It will search from the current subdomain of context's host, if not inside the root domain.
+ RouteExists(method, path string) bool
// Application returns the iris app instance which belongs to this context.
// Worth to notice that this function returns an interface
@@ -1260,7 +1294,39 @@ func (ctx *context) Next() { // or context.Next(ctx)
Next(ctx)
}
-// NextHandler returns, but it doesn't executes, the next handler from the handlers chain.
+// NextOr checks if chain has a next handler, if so then it executes it
+// otherwise it sets a new chain assigned to this Context based on the given handler(s)
+// and executes its first handler.
+//
+// Returns true if next handler exists and executed, otherwise false.
+//
+// Note that if no next handler found and handlers are missing then
+// it sends a Status Not Found (404) to the client and it stops the execution.
+func (ctx *context) NextOr(handlers ...Handler) bool {
+ if next := ctx.NextHandler(); next != nil {
+ next(ctx)
+ ctx.Skip() // skip this handler from the chain.
+ return true
+ }
+
+ if len(handlers) == 0 {
+ ctx.NotFound()
+ ctx.StopExecution()
+ return false
+ }
+
+ ctx.Do(handlers)
+
+ return false
+}
+
+// NextOrNotFound checks if chain has a next handler, if so then it executes it
+// otherwise it sends a Status Not Found (404) to the client and stops the execution.
+//
+// Returns true if next handler exists and executed, otherwise false.
+func (ctx *context) NextOrNotFound() bool { return ctx.NextOr() }
+
+// NextHandler returns (it doesn't execute) the next handler from the handlers chain.
//
// Use .Skip() to skip this handler if needed to execute the next of this returning handler.
func (ctx *context) NextHandler() Handler {
@@ -1884,6 +1950,8 @@ func (ctx *context) PostValues(name string) []string {
//
// The default form's memory maximum size is 32MB, it can be changed by the
// `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
+//
+// Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-file
func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
// we don't have access to see if the request is body stream
// and then the ParseMultipartForm can be useless
@@ -1917,6 +1985,8 @@ func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader,
// `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
//
// See `FormFile` to a more controlled to receive a file.
+//
+// Example: https://github.com/kataras/iris/tree/master/_examples/http_request/upload-files
func (ctx *context) UploadFormFiles(destDirectory string, before ...func(Context, *multipart.FileHeader)) (n int64, err error) {
err = ctx.request.ParseMultipartForm(ctx.Application().ConfigurationReadOnly().GetPostMaxMemory())
if err != nil {
@@ -2008,7 +2078,9 @@ func (ctx *context) SetMaxRequestBodySize(limitOverBytes int64) {
// UnmarshalBody reads the request's body and binds it to a value or pointer of any type
// Examples of usage: context.ReadJSON, context.ReadXML.
-func (ctx *context) UnmarshalBody(v interface{}, unmarshaler Unmarshaler) error {
+//
+// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go
+func (ctx *context) UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) error {
if ctx.request.Body == nil {
return errors.New("unmarshal: empty body")
}
@@ -2028,18 +2100,19 @@ func (ctx *context) UnmarshalBody(v interface{}, unmarshaler Unmarshaler) error
// in this case the v should be a pointer also,
// but this is up to the user's custom Decode implementation*
//
- // See 'BodyDecoder' for more
- if decoder, isDecoder := v.(BodyDecoder); isDecoder {
+ // See 'BodyDecoder' for more.
+ if decoder, isDecoder := outPtr.(BodyDecoder); isDecoder {
return decoder.Decode(rawData)
}
- // check if v is already a pointer, if yes then pass as it's
- if reflect.TypeOf(v).Kind() == reflect.Ptr {
- return unmarshaler.Unmarshal(rawData, v)
- }
- // finally, if the v doesn't contains a self-body decoder and it's not a pointer
- // use the custom unmarshaler to bind the body
- return unmarshaler.Unmarshal(rawData, &v)
+ // // check if v is already a pointer, if yes then pass as it's
+ // if reflect.TypeOf(v).Kind() == reflect.Ptr {
+ // return unmarshaler.Unmarshal(rawData, v)
+ // } <- no need for that, ReadJSON is documented enough to receive a pointer,
+ // we don't need to reduce the performance here by using the reflect.TypeOf method.
+
+ // f the v doesn't contains a self-body decoder use the custom unmarshaler to bind the body.
+ return unmarshaler.Unmarshal(rawData, outPtr)
}
func (ctx *context) shouldOptimize() bool {
@@ -2047,6 +2120,8 @@ func (ctx *context) shouldOptimize() bool {
}
// ReadJSON reads JSON from request's body and binds it to a value of any json-valid type.
+//
+// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-json/main.go
func (ctx *context) ReadJSON(jsonObject interface{}) error {
var unmarshaler = json.Unmarshal
if ctx.shouldOptimize() {
@@ -2056,6 +2131,8 @@ func (ctx *context) ReadJSON(jsonObject interface{}) error {
}
// ReadXML reads XML from request's body and binds it to a value of any xml-valid type.
+//
+// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-xml/main.go
func (ctx *context) ReadXML(xmlObject interface{}) error {
return ctx.UnmarshalBody(xmlObject, UnmarshalerFunc(xml.Unmarshal))
}
@@ -2066,6 +2143,8 @@ var (
// ReadForm binds the formObject with the form data
// it supports any kind of struct.
+//
+// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-form/main.go
func (ctx *context) ReadForm(formObject interface{}) error {
values := ctx.FormValues()
if values == nil {
@@ -3086,48 +3165,57 @@ func (ctx *context) TransactionsSkipped() bool {
//
// It's for extreme use cases, 99% of the times will never be useful for you.
func (ctx *context) Exec(method string, path string) {
- if path != "" {
- if method == "" {
- method = "GET"
- }
-
- // backup the handlers
- backupHandlers := ctx.Handlers()[0:]
- backupPos := ctx.HandlerIndex(-1)
-
- // backup the request path information
- backupPath := ctx.Path()
- bakcupMethod := ctx.Method()
- // don't backupValues := ctx.Values().ReadOnly()
-
- // [sessions stays]
- // [values stays]
- // reset handlers
- ctx.SetHandlers(nil)
-
- req := ctx.Request()
- // set the request to be align with the 'againstRequestPath'
- req.RequestURI = path
- req.URL.Path = path
- req.Method = method
- // execute the route from the (internal) context router
- // this way we keep the sessions and the values
- ctx.Application().ServeHTTPC(ctx)
-
- // set back the old handlers and the last known index
- ctx.SetHandlers(backupHandlers)
- ctx.HandlerIndex(backupPos)
- // set the request back to its previous state
- req.RequestURI = backupPath
- req.URL.Path = backupPath
- req.Method = bakcupMethod
-
- // don't fill the values in order to be able to communicate from and to.
- // // fill the values as they were before
- // backupValues.Visit(func(key string, value interface{}) {
- // ctx.Values().Set(key, value)
- // })
+ if path == "" {
+ return
}
+
+ if method == "" {
+ method = "GET"
+ }
+
+ // backup the handlers
+ backupHandlers := ctx.Handlers()[0:]
+ backupPos := ctx.HandlerIndex(-1)
+
+ // backup the request path information
+ backupPath := ctx.Path()
+ backupMethod := ctx.Method()
+ // don't backupValues := ctx.Values().ReadOnly()
+
+ // [values stays]
+ // reset handlers
+ ctx.SetHandlers(nil)
+
+ req := ctx.Request()
+ // set the request to be align with the 'againstRequestPath'
+ req.RequestURI = path
+ req.URL.Path = path
+ req.Method = method
+ req.Host = req.Host
+
+ // execute the route from the (internal) context router
+ // this way we keep the sessions and the values
+ ctx.Application().ServeHTTPC(ctx)
+
+ // set back the old handlers and the last known index
+ ctx.SetHandlers(backupHandlers)
+ ctx.HandlerIndex(backupPos)
+ // set the request back to its previous state
+ req.RequestURI = backupPath
+ req.URL.Path = backupPath
+ req.Method = backupMethod
+
+ // don't fill the values in order to be able to communicate from and to.
+ // // fill the values as they were before
+ // backupValues.Visit(func(key string, value interface{}) {
+ // ctx.Values().Set(key, value)
+ // })
+}
+
+// RouteExists reports whether a particular route exists
+// It will search from the current subdomain of context's host, if not inside the root domain.
+func (ctx *context) RouteExists(method, path string) bool {
+ return ctx.Application().RouteExists(ctx, method, path)
}
// Application returns the iris app instance which belongs to this context.
diff --git a/core/maintenance/version.go b/core/maintenance/version.go
index 82648e6e..3057af63 100644
--- a/core/maintenance/version.go
+++ b/core/maintenance/version.go
@@ -13,7 +13,7 @@ import (
const (
// Version is the string representation of the current local Iris Web Framework version.
- Version = "10.2.1"
+ Version = "10.3.0"
)
// CheckForUpdates checks for any available updates
diff --git a/core/memstore/memstore.go b/core/memstore/memstore.go
index 2e161560..0beec0e6 100644
--- a/core/memstore/memstore.go
+++ b/core/memstore/memstore.go
@@ -180,7 +180,7 @@ func (e Entry) BoolDefault(def bool) (bool, error) {
// respects the immutable.
func (e Entry) Value() interface{} {
if e.immutable {
- // take its value, no pointer even if setted with a rreference.
+ // take its value, no pointer even if setted with a reference.
vv := reflect.Indirect(reflect.ValueOf(e.ValueRaw))
// return copy of that slice
diff --git a/core/router/api_builder.go b/core/router/api_builder.go
index e9afefba..739641a1 100644
--- a/core/router/api_builder.go
+++ b/core/router/api_builder.go
@@ -42,6 +42,12 @@ type repository struct {
}
func (r *repository) register(route *Route) {
+ for _, r := range r.routes {
+ if r.String() == route.String() {
+ return // do not register any duplicates, the sooner the better.
+ }
+ }
+
r.routes = append(r.routes, route)
}
@@ -92,10 +98,15 @@ type APIBuilder struct {
doneGlobalHandlers context.Handlers
// the per-party
relativePath string
+ // allowMethods are filled with the `AllowMethods` func.
+ // They are used to create new routes
+ // per any party's (and its children) routes registered
+ // if the method "x" wasn't registered already via the `Handle` (and its extensions like `Get`, `Post`...).
+ allowMethods []string
}
-var _ Party = &APIBuilder{}
-var _ RoutesProvider = &APIBuilder{} // passed to the default request handler (routerHandler)
+var _ Party = (*APIBuilder)(nil)
+var _ RoutesProvider = (*APIBuilder)(nil) // passed to the default request handler (routerHandler)
// NewAPIBuilder creates & returns a new builder
// which is responsible to build the API and the router handler.
@@ -129,6 +140,16 @@ func (api *APIBuilder) GetReporter() *errors.Reporter {
return api.reporter
}
+// AllowMethods will re-register the future routes that will be registered
+// via `Handle`, `Get`, `Post`, ... to the given "methods" on that Party and its children "Parties",
+// duplicates are not registered.
+//
+// Call of `AllowMethod` will override any previous allow methods.
+func (api *APIBuilder) AllowMethods(methods ...string) Party {
+ api.allowMethods = methods
+ return api
+}
+
// Handle registers a route to the server's api.
// if empty method is passed then handler(s) are being registered to all methods, same as .Any.
//
@@ -170,23 +191,30 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
// here we separate the subdomain and relative path
subdomain, path := splitSubdomainAndPath(fullpath)
- r, err := NewRoute(method, subdomain, path, possibleMainHandlerName, routeHandlers, api.macros)
- if err != nil { // template path parser errors:
- api.reporter.Add("%v -> %s:%s:%s", err, method, subdomain, path)
- return nil
+ // if allowMethods are empty, then simply register with the passed, main, method.
+ methods := append(api.allowMethods, method)
+
+ var (
+ route *Route // the latest one is this route registered, see methods append.
+ err error // not used outside of loop scope.
+ )
+
+ for _, m := range methods {
+ route, err = NewRoute(m, subdomain, path, possibleMainHandlerName, routeHandlers, api.macros)
+ if err != nil { // template path parser errors:
+ api.reporter.Add("%v -> %s:%s:%s", err, method, subdomain, path)
+ return nil // fail on first error.
+ }
+
+ // Add UseGlobal & DoneGlobal Handlers
+ route.use(api.beginGlobalHandlers)
+ route.done(api.doneGlobalHandlers)
+
+ // global
+ api.routes.register(route)
}
- // Add UseGlobal & DoneGlobal Handlers
- r.use(api.beginGlobalHandlers)
- r.done(api.doneGlobalHandlers)
-
- // global
- api.routes.register(r)
-
- // per -party, used for done handlers
- // api.apiRoutes = append(api.apiRoutes, r)
-
- return r
+ return route
}
// HandleMany works like `Handle` but can receive more than one
@@ -259,6 +287,10 @@ func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) P
// append the parent's + child's handlers
middleware := joinHandlers(api.middleware, handlers)
+ // the allow methods per party and its children.
+ allowMethods := make([]string, len(api.allowMethods))
+ copy(allowMethods, api.allowMethods)
+
return &APIBuilder{
// global/api builder
macros: api.macros,
@@ -269,8 +301,9 @@ func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) P
reporter: api.reporter,
// per-party/children
middleware: middleware,
- doneHandlers: api.doneHandlers,
+ doneHandlers: api.doneHandlers[0:],
relativePath: fullpath,
+ allowMethods: allowMethods,
}
}
diff --git a/core/router/handler.go b/core/router/handler.go
index bb00eb3a..323e693e 100644
--- a/core/router/handler.go
+++ b/core/router/handler.go
@@ -22,6 +22,8 @@ type RequestHandler interface {
HandleRequest(context.Context)
// Build should builds the handler, it's being called on router's BuildRouter.
Build(provider RoutesProvider) error
+ // RouteExists reports whether a particular route exists.
+ RouteExists(ctx context.Context, method, path string) bool
}
type tree struct {
@@ -160,6 +162,14 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
r.URL.Path = path
url := r.URL.String()
+ // Fixes https://github.com/kataras/iris/issues/921
+ // This is caused for security reasons, imagine a payment shop,
+ // you can't just permantly redirect a POST request, so just 307 (RFC 7231, 6.4.7).
+ if method == http.MethodPost || method == http.MethodPut {
+ ctx.Redirect(url, http.StatusTemporaryRedirect)
+ return
+ }
+
ctx.Redirect(url, http.StatusMovedPermanently)
// RFC2616 recommends that a short note "SHOULD" be included in the
@@ -244,3 +254,57 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
ctx.StatusCode(http.StatusNotFound)
}
+
+// RouteExists reports whether a particular route exists
+// It will search from the current subdomain of context's host, if not inside the root domain.
+func (h *routerHandler) RouteExists(ctx context.Context, method, path string) bool {
+ for i := range h.trees {
+ t := h.trees[i]
+ if method != t.Method {
+ continue
+ }
+
+ if h.hosts && t.Subdomain != "" {
+ requestHost := ctx.Host()
+ if netutil.IsLoopbackSubdomain(requestHost) {
+ // this fixes a bug when listening on
+ // 127.0.0.1:8080 for example
+ // and have a wildcard subdomain and a route registered to root domain.
+ continue // it's not a subdomain, it's something like 127.0.0.1 probably
+ }
+ // it's a dynamic wildcard subdomain, we have just to check if ctx.subdomain is not empty
+ if t.Subdomain == SubdomainWildcardIndicator {
+ // mydomain.com -> invalid
+ // localhost -> invalid
+ // sub.mydomain.com -> valid
+ // sub.localhost -> valid
+ serverHost := ctx.Application().ConfigurationReadOnly().GetVHost()
+ if serverHost == requestHost {
+ continue // it's not a subdomain, it's a full domain (with .com...)
+ }
+
+ dotIdx := strings.IndexByte(requestHost, '.')
+ slashIdx := strings.IndexByte(requestHost, '/')
+ if dotIdx > 0 && (slashIdx == -1 || slashIdx > dotIdx) {
+ // if "." was found anywhere but not at the first path segment (host).
+ } else {
+ continue
+ }
+ // continue to that, any subdomain is valid.
+ } else if !strings.HasPrefix(requestHost, t.Subdomain) { // t.Subdomain contains the dot.
+ continue
+ }
+ }
+
+ _, handlers := t.Nodes.Find(path, ctx.Params())
+ if len(handlers) > 0 {
+ // found
+ return true
+ }
+
+ // not found or method not allowed.
+ break
+ }
+
+ return false
+}
diff --git a/core/router/party.go b/core/router/party.go
index 6f0e814e..c83f508a 100644
--- a/core/router/party.go
+++ b/core/router/party.go
@@ -6,9 +6,6 @@ import (
"github.com/kataras/iris/core/router/macro"
)
-// Party is here to separate the concept of
-// api builder and the sub api builder.
-
// Party is just a group joiner of routes which have the same prefix and share same middleware(s) also.
// Party could also be named as 'Join' or 'Node' or 'Group' , Party chosen because it is fun.
//
@@ -68,6 +65,14 @@ type Party interface {
//
// Returns this Party.
Reset() Party
+
+ // AllowMethods will re-register the future routes that will be registered
+ // via `Handle`, `Get`, `Post`, ... to the given "methods" on that Party and its children "Parties",
+ // duplicates are not registered.
+ //
+ // Call of `AllowMethod` will override any previous allow methods.
+ AllowMethods(methods ...string) Party
+
// Handle registers a route to the server's router.
// if empty method is passed then handler(s) are being registered to all methods, same as .Any.
//
diff --git a/core/router/path.go b/core/router/path.go
index 51b1716f..333b1ea6 100644
--- a/core/router/path.go
+++ b/core/router/path.go
@@ -7,6 +7,7 @@ import (
"strings"
"github.com/kataras/iris/core/netutil"
+ "github.com/kataras/iris/core/router/macro/interpreter/lexer"
)
const (
@@ -73,30 +74,91 @@ func joinPath(path1 string, path2 string) string {
// iteratively until no further processing can be done:
//
// 1. Replace multiple slashes with a single slash.
-// 3. Eliminate each inner .. path name element (the parent directory)
-// along with the non-.. element that precedes it.
-// 4. Eliminate .. elements that begin a rooted path:
-// that is, replace "/.." by "/" at the beginning of a path.
+// 2. Replace '\' with '/'
+// 3. Replace "\\" with '/'
+// 4. Ignore anything inside '{' and '}'
+// 5. Makes sure that prefixed with '/'
+// 6. Remove trailing '/'.
//
// The returned path ends in a slash only if it is the root "/".
func cleanPath(s string) string {
+ // note that we don't care about the performance here, it's before the server ran.
if s == "" || s == "." {
return "/"
}
- // remove suffix "/"
+ // remove suffix "/".
if lidx := len(s) - 1; s[lidx] == '/' {
s = s[:lidx]
}
- // prefix with "/"
+ // prefix with "/".
s = prefix(s, "/")
- // remove the os specific dir sep
- s = strings.Replace(s, "\\", "/", -1)
+ // If you're learning go through Iris I will ask you to ignore the
+ // following part, it's not the recommending way to do that,
+ // but it's understable to me.
+ var (
+ insideMacro = false
+ i = -1
+ )
- // use std path to clean the path
- s = path.Clean(s)
+ for {
+ i++
+ if len(s) <= i {
+ break
+ }
+
+ if s[i] == lexer.Begin {
+ insideMacro = true
+ continue
+ }
+
+ if s[i] == lexer.End {
+ insideMacro = false
+ continue
+ }
+
+ // when inside {} then don't try to clean it.
+ if !insideMacro {
+ if s[i] == '/' {
+ if len(s)-1 >= i+1 && s[i+1] == '/' { // we have "//".
+ bckp := s
+ s = bckp[:i] + "/"
+ // forward two, we ignore the second "/" in the raw.
+ i = i + 2
+ if len(bckp)-1 >= i {
+ s += bckp[i:]
+ }
+ }
+ // if we have just a single slash then continue.
+ continue
+ }
+
+ if s[i] == '\\' { // this will catch "\\" and "\".
+ bckp := s
+ s = bckp[:i] + "/"
+
+ if len(bckp)-1 >= i+1 {
+ s += bckp[i+1:]
+ i++
+ }
+
+ if len(s)-1 > i && s[i] == '\\' {
+ bckp := s
+ s = bckp[:i]
+ if len(bckp)-1 >= i+2 {
+ s = bckp[:i-1] + bckp[i+1:]
+ i++
+ }
+ }
+
+ continue
+ }
+
+ }
+
+ }
return s
}
diff --git a/core/router/path_test.go b/core/router/path_test.go
index 28c49b13..33a00626 100644
--- a/core/router/path_test.go
+++ b/core/router/path_test.go
@@ -4,6 +4,38 @@ import (
"testing"
)
+func TestCleanPath(t *testing.T) {
+ tests := []struct {
+ path string
+ expected string
+ }{
+ {"noslashPrefix",
+ "/noslashPrefix"},
+ {"slashSuffix/",
+ "/slashSuffix"},
+ {"noSlashPrefixAndslashSuffix/",
+ "/noSlashPrefixAndslashSuffix"},
+ // don't do any clean up inside {},
+ // fixes #927.
+ {"/total/{year:string regexp(\\d{4})}",
+ "/total/{year:string regexp(\\d{4})}"},
+ {"/total/{year:string regexp(\\d{4})}/more",
+ "/total/{year:string regexp(\\d{4})}/more"},
+ {"/total/{year:string regexp(\\d{4})}/more/{s:string regexp(\\d{7})}",
+ "/total/{year:string regexp(\\d{4})}/more/{s:string regexp(\\d{7})}"},
+ {"/single_no_params",
+ "/single_no_params"},
+ {"/single/{id:int}",
+ "/single/{id:int}"},
+ }
+
+ for i, tt := range tests {
+ if expected, got := tt.expected, cleanPath(tt.path); expected != got {
+ t.Fatalf("[%d] - expected path '%s' but got '%s'", i, expected, got)
+ }
+ }
+}
+
func TestSplitPath(t *testing.T) {
tests := []struct {
path string
@@ -50,8 +82,8 @@ func TestSplitSubdomainAndPath(t *testing.T) {
}{
{"admin./users/42", "admin.", "/users/42"},
{"//api/users\\42", "", "/api/users/42"},
- {"admin./users/\\42", "admin.", "/users/42"},
- {"*./users/\\42", "*.", "/users/42"},
+ {"admin./users//42", "admin.", "/users/42"},
+ {"*./users/42/", "*.", "/users/42"},
}
for i, tt := range tests {
diff --git a/core/router/router.go b/core/router/router.go
index 9b81d2ea..50526395 100644
--- a/core/router/router.go
+++ b/core/router/router.go
@@ -147,6 +147,12 @@ func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
router.mainHandler(w, r)
}
+// RouteExists reports whether a particular route exists
+// It will search from the current subdomain of context's host, if not inside the root domain.
+func (router *Router) RouteExists(ctx context.Context, method, path string) bool {
+ return router.requestHandler.RouteExists(ctx, method, path)
+}
+
type wrapper struct {
router http.HandlerFunc // http.HandlerFunc to catch the CURRENT state of its .ServeHTTP on case of future change.
wrapperFunc func(http.ResponseWriter, *http.Request, http.HandlerFunc)
diff --git a/core/router/router_test.go b/core/router/router_test.go
new file mode 100644
index 00000000..ba41a714
--- /dev/null
+++ b/core/router/router_test.go
@@ -0,0 +1,41 @@
+package router_test
+
+import (
+ "testing"
+
+ "github.com/kataras/iris"
+ "github.com/kataras/iris/context"
+ "github.com/kataras/iris/httptest"
+)
+
+func TestRouteExists(t *testing.T) {
+ // build the api
+ app := iris.New()
+ emptyHandler := func(context.Context) {}
+
+ // setup the tested routes
+ app.Handle("GET", "/route-exists", emptyHandler)
+ app.Handle("POST", "/route-with-param/{param}", emptyHandler)
+
+ // check RouteExists
+ app.Handle("GET", "/route-test", func(ctx context.Context) {
+ if ctx.RouteExists("GET", "/route-not-exists") {
+ t.Error("Route with path should not exists")
+ }
+
+ if ctx.RouteExists("POST", "/route-exists") {
+ t.Error("Route with method should not exists")
+ }
+
+ if !ctx.RouteExists("GET", "/route-exists") {
+ t.Error("Route 1 should exists")
+ }
+
+ if !ctx.RouteExists("POST", "/route-with-param/a-param") {
+ t.Error("Route 2 should exists")
+ }
+ })
+
+ // run the tests
+ httptest.New(t, app, httptest.Debug(false)).Request("GET", "/route-test").Expect().Status(iris.StatusOK)
+}
diff --git a/doc.go b/doc.go
index 4ae3be38..7bbdd4a3 100644
--- a/doc.go
+++ b/doc.go
@@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
Current Version
-10.0.0
+10.3.0
Installation
@@ -1121,7 +1121,7 @@ Example code:
-View engine supports bundled(https://github.com/jteeuwen/go-bindata) template files too.
+View engine supports bundled(https://github.com/shuLhan/go-bindata) template files too.
go-bindata gives you two functions, asset and assetNames,
these can be setted to each of the template engines using the `.Binary` func.
@@ -1133,7 +1133,7 @@ Example code:
func main() {
app := iris.New()
- // $ go get -u github.com/jteeuwen/go-bindata/...
+ // $ go get -u github.com/shuLhan/go-bindata/...
// $ go-bindata ./templates/...
// $ go build
// $ ./embedding-templates-into-app
diff --git a/go19.go b/go19.go
index 8a16665e..adcd8a4f 100644
--- a/go19.go
+++ b/go19.go
@@ -17,6 +17,16 @@ type (
// Developers send responses to the client's request through a Context.
// Developers get request information from the client's request by a Context.
Context = context.Context
+ // UnmarshalerFunc a shortcut, an alias for the `context#UnmarshalerFunc` type
+ // which implements the `context#Unmarshaler` interface for reading request's body
+ // via custom decoders, most of them already implement the `context#UnmarshalerFunc`
+ // like the json.Unmarshal, xml.Unmarshal, yaml.Unmarshal and every library which
+ // follows the best practises and is aligned with the Go standards.
+ //
+ // See 'context#UnmarshalBody` for more.
+ //
+ // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go
+ UnmarshalerFunc = context.UnmarshalerFunc
// A Handler responds to an HTTP request.
// It writes reply headers and data to the Context.ResponseWriter() and then return.
// Returning signals that the request is finished;
diff --git a/sessions/sessiondb/redis/service/service.go b/sessions/sessiondb/redis/service/service.go
index e48e63de..34d4f8db 100644
--- a/sessions/sessiondb/redis/service/service.go
+++ b/sessions/sessiondb/redis/service/service.go
@@ -128,10 +128,9 @@ func (r *Service) GetBytes(key string) ([]byte, error) {
func (r *Service) Delete(key string) error {
c := r.pool.Get()
defer c.Close()
- if _, err := c.Do("DEL", r.Config.Prefix+key); err != nil {
- return err
- }
- return nil
+
+ _, err := c.Do("DEL", r.Config.Prefix+key)
+ return err
}
func dial(network string, addr string, pass string) (redis.Conn, error) {
diff --git a/view/README.md b/view/README.md
index e054a77e..0ce6e74f 100644
--- a/view/README.md
+++ b/view/README.md
@@ -112,7 +112,7 @@ func hi(ctx iris.Context) {
## Embedded
-View engine supports bundled(https://github.com/jteeuwen/go-bindata) template files too.
+View engine supports bundled(https://github.com/shuLhan/go-bindata) template files too.
`go-bindata` gives you two functions, `Assset` and `AssetNames`,
these can be setted to each of the template engines using the `.Binary` function.
@@ -125,7 +125,7 @@ import "github.com/kataras/iris"
func main() {
app := iris.New()
- // $ go get -u github.com/jteeuwen/go-bindata/...
+ // $ go get -u github.com/shuLhan/go-bindata/...
// $ go-bindata ./templates/...
// $ go build
// $ ./embedding-templates-into-app
diff --git a/view/django.go b/view/django.go
index 47817b98..d989a1a2 100644
--- a/view/django.go
+++ b/view/django.go
@@ -44,6 +44,18 @@ type (
TagParser func(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error)
)
+// AsValue converts any given value to a view.Value.
+// Usually being used within own functions passed to a template
+// through a Context or within filter functions.
+func AsValue(i interface{}) *Value {
+ return (*Value)(pongo2.AsValue(i))
+}
+
+// AsSafeValue works like AsValue, but does not apply the 'escape' filter.
+func AsSafeValue(i interface{}) *Value {
+ return (*Value)(pongo2.AsSafeValue(i))
+}
+
// GetValue returns the `Value` as *pongo2.Value type.
// This method was added by balthild at https://github.com/kataras/iris/pull/765
func (value *Value) GetValue() *pongo2.Value {
diff --git a/view/handlebars.go b/view/handlebars.go
index bfede2be..68b1fcee 100644
--- a/view/handlebars.go
+++ b/view/handlebars.go
@@ -128,7 +128,7 @@ func (s *HandlebarsEngine) loadDirectory() error {
// instead of the html/template engine which works like {{ render "myfile.html"}} and accepts the parent binding, with handlebars we can't do that because of lack of runtime helpers (dublicate error)
var templateErr error
- filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+ filepath.Walk(dir, func(path string, info os.FileInfo, _ error) error {
if info == nil || info.IsDir() {
return nil
}
diff --git a/websocket/server.go b/websocket/server.go
index a08b3e1a..44120b90 100644
--- a/websocket/server.go
+++ b/websocket/server.go
@@ -147,6 +147,9 @@ func New(cfg Config) *Server {
func (s *Server) Handler() context.Handler {
return func(ctx context.Context) {
c := s.Upgrade(ctx)
+ if c.Err() != nil {
+ return
+ }
// NOTE TO ME: fire these first BEFORE startReader and startPinger
// in order to set the events and any messages to send
// the startPinger will send the OK to the client and only