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