Merge pull request #10 from kataras/master

Update

Former-commit-id: 6c5ffc3c8f7a44929db41a7a7457a77cd1029f2d
This commit is contained in:
Zeno-Code 2018-03-14 00:55:51 +08:00 committed by GitHub
commit 06f75795bb
47 changed files with 838 additions and 186 deletions

View File

@ -3,7 +3,8 @@ os:
- linux - linux
- osx - osx
go: go:
- go1.9 - "go1.9"
- "go1.10"
go_import_path: github.com/kataras/iris go_import_path: github.com/kataras/iris
install: install:
- go get ./... # for iris-contrib/httpexpect, kataras/golog - go get ./... # for iris-contrib/httpexpect, kataras/golog

10
Gopkg.lock generated
View File

@ -107,7 +107,7 @@
branch = "master" branch = "master"
name = "github.com/kataras/golog" name = "github.com/kataras/golog"
packages = ["."] packages = ["."]
revision = "2ed680e7b1f34147164fa8073373e14fce02ac30" revision = "dd676348ce75fa471fbbcd1bbbd131d00179756a"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -115,12 +115,6 @@
packages = [".","terminal"] packages = [".","terminal"]
revision = "825e39f34365e7db2c9fbc3692c16220e3bd7418" revision = "825e39f34365e7db2c9fbc3692c16220e3bd7418"
[[projects]]
branch = "master"
name = "github.com/kataras/survey"
packages = ["."]
revision = "20e139a6d2469769ae88e0a3579ba5df71839ca7"
[[projects]] [[projects]]
name = "github.com/klauspost/compress" name = "github.com/klauspost/compress"
packages = ["flate","gzip"] packages = ["flate","gzip"]
@ -251,7 +245,7 @@
branch = "master" branch = "master"
name = "golang.org/x/sys" name = "golang.org/x/sys"
packages = ["unix"] packages = ["unix"]
revision = "2d6f6f883a06fc0d5f4b14a81e4c28705ea64c15" revision = "c28acc882ebcbfbe8ce9f0f14b9ac26ee138dd51"
[[projects]] [[projects]]
branch = "master" branch = "master"

View File

@ -42,10 +42,6 @@
branch = "master" branch = "master"
name = "github.com/kataras/golog" name = "github.com/kataras/golog"
[[constraint]]
branch = "master"
name = "github.com/kataras/survey"
[[constraint]] [[constraint]]
name = "github.com/klauspost/compress" name = "github.com/klauspost/compress"
version = "1.2.1" version = "1.2.1"

View File

@ -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. **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 # 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). 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).

View File

@ -17,6 +17,10 @@
**Πώς να αναβαθμίσετε**: Ανοίξτε την γραμμή εντολών σας και εκτελέστε αυτήν την εντολή: `go get -u github.com/kataras/iris` ή αφήστε το αυτόματο updater να το κάνει αυτό για σας. **Πώς να αναβαθμίσετε**: Ανοίξτε την γραμμή εντολών σας και εκτελέστε αυτήν την εντολή: `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 # 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). Διόρθωση το οποίο αφορά 404 not found errors στα αρχεία που σερβίρονται από τα `StaticEmbedded` και `StaticWeb` των υποτομεών(subdomains), όπως αναφέρθηκε πριν λίγο από τον [@speedwheel](https://github.com/speedwheel) μέσω [της σελίδας μας στο facebook](https://facebook.com/iris.framework).

View File

@ -17,6 +17,10 @@
**如何升级**: 打开命令行执行以下命令: `go get -u github.com/kataras/iris` 或者等待自动更新。 **如何升级**: 打开命令行执行以下命令: `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 版本更新 # 2018 2月15号 | v10.2.1 版本更新
修正 子域名 (subdomain) 的 `StaticEmbedded``StaticWeb` 不存在错误, 由 [@speedwheel](https://github.com/speedwheel) 通过 [facebook page's chat](https://facebook.com/iris.framework) 反馈。 修正 子域名 (subdomain) 的 `StaticEmbedded``StaticWeb` 不存在错误, 由 [@speedwheel](https://github.com/speedwheel) 通过 [facebook page's chat](https://facebook.com/iris.framework) 反馈。

View File

@ -2,7 +2,7 @@
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a> <a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![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)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![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)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![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)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![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. 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 ## 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) - 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) - 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) - 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 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 ### Articles

View File

@ -2,7 +2,7 @@
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a> <a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![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)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![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)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![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)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![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. Το 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) - Μήπως τυχαίνει να βρήκατε κάποιο bug; Δημοσιεύστε το στα [github issues](https://github.com/kataras/iris/issues)
- Έχετε οποιεσδήποτε ερωτήσεις ή πρέπει να μιλήσετε με κάποιον έμπειρο για την επίλυση ενός προβλήματος σε πραγματικό χρόνο; Ελάτε μαζί μας στην [συνομιλία κοινότητας](https://chat.iris-go.com) - Έχετε οποιεσδήποτε ερωτήσεις ή πρέπει να μιλήσετε με κάποιον έμπειρο για την επίλυση ενός προβλήματος σε πραγματικό χρόνο; Ελάτε μαζί μας στην [συνομιλία κοινότητας](https://chat.iris-go.com)
- Συμπληρώστε την αναφορά εμπειρίας χρήστη κάνοντας κλικ [εδώ](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link) - Συμπληρώστε την αναφορά εμπειρίας χρήστη κάνοντας κλικ [εδώ](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)

View File

@ -2,7 +2,7 @@
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a> <a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![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)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![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)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![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)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![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. 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) - Вы случайно обнаружили ошибку? Опубликуйте ее на [Github вопросы](https://github.com/kataras/iris/issues)
- У Вас есть какие-либо вопросы или Вам нужно поговорить с кем-то, кто бы смог решить Вашу проблему в режиме реального времени? Присоединяйтесь к нам в [чате сообщества](https://chat.iris-go.com) - У Вас есть какие-либо вопросы или Вам нужно поговорить с кем-то, кто бы смог решить Вашу проблему в режиме реального времени? Присоединяйтесь к нам в [чате сообщества](https://chat.iris-go.com)
- Заполните наш отчет о пользовательском опыте на основе формы, нажав [здесь](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link) - Заполните наш отчет о пользовательском опыте на основе формы, нажав [здесь](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)

View File

@ -2,7 +2,7 @@
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a> <a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![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)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![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)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![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)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![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开发框架。 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) - 你碰巧找到了一个错误? 请提交 [github issues](https://github.com/kataras/iris/issues)
- 您是否有任何疑问或需要与有经验的人士交谈以实时解决问题? [加入我们的聊天](https://chat.iris-go.com) - 您是否有任何疑问或需要与有经验的人士交谈以实时解决问题? [加入我们的聊天](https://chat.iris-go.com)
- [点击这里完成我们基于表单的用户体验报告](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link) - [点击这里完成我们基于表单的用户体验报告](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)

View File

@ -1 +1 @@
10.2.1:https://github.com/kataras/iris/blob/master/HISTORY.md#th-15-february-2018--v1021 10.3.0:https://github.com/kataras/iris/blob/master/HISTORY.md#sa-10-march-2018--v1030

View File

@ -318,8 +318,11 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her
### How to Read from `context.Request() *http.Request` ### How to Read from `context.Request() *http.Request`
- [Bind JSON](http_request/read-json/main.go) - [Read JSON](http_request/read-json/main.go)
- [Bind Form](http_request/read-form/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/Read File](http_request/upload-file/main.go)
- [Upload multiple files with an easy way](http_request/upload-files/main.go) - [Upload multiple files with an easy way](http_request/upload-files/main.go)

View File

@ -1,7 +1,6 @@
package main package main
// $ go get github.com/rs/cors // go get -u github.com/iris-contrib/middleware/...
// $ go run main.go
import ( import (
"github.com/kataras/iris" "github.com/kataras/iris"
@ -10,15 +9,14 @@ import (
) )
func main() { func main() {
app := iris.New() app := iris.New()
crs := cors.New(cors.Options{ crs := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, // allows everything, use that to change the hosts. AllowedOrigins: []string{"*"}, // allows everything, use that to change the hosts.
AllowCredentials: true, AllowCredentials: true,
}) })
v1 := app.Party("/api/v1") v1 := app.Party("/api/v1", crs).AllowMethods(iris.MethodOptions) // <- important for the preflight.
v1.Use(crs)
{ {
v1.Get("/home", func(ctx iris.Context) { v1.Get("/home", func(ctx iris.Context) {
ctx.WriteString("Hello from /home") ctx.WriteString("Hello from /home")
@ -29,15 +27,13 @@ func main() {
v1.Post("/send", func(ctx iris.Context) { v1.Post("/send", func(ctx iris.Context) {
ctx.WriteString("sent") 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")) app.Run(iris.Addr("localhost:8080"))
} }

View File

@ -5,7 +5,7 @@ import (
) )
// Follow these steps first: // Follow these steps first:
// $ go get -u github.com/jteeuwen/go-bindata/... // $ go get -u github.com/shuLhan/go-bindata/...
// $ go-bindata ./assets/... // $ go-bindata ./assets/...
// $ go build // $ go build
// $ ./embedding-files-into-app // $ ./embedding-files-into-app

View File

@ -2,7 +2,7 @@ package main
import "github.com/kataras/iris" 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-bindata ./public/...
// $ go build // $ go build
// $ ./embedded-single-page-application-with-other-routes // $ ./embedded-single-page-application-with-other-routes

View File

@ -4,7 +4,7 @@ import (
"github.com/kataras/iris" "github.com/kataras/iris"
) )
// $ go get -u github.com/jteeuwen/go-bindata/... // $ go get -u github.com/shuLhan/go-bindata/...
// $ go-bindata ./public/... // $ go-bindata ./public/...
// $ go build // $ go build
// $ ./embedded-single-page-application // $ ./embedded-single-page-application

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -11,16 +11,18 @@ type Company struct {
} }
func MyHandler(ctx iris.Context) { func MyHandler(ctx iris.Context) {
c := &Company{} var c Company
if err := ctx.ReadJSON(c); err != nil {
if err := ctx.ReadJSON(&c); err != nil {
ctx.StatusCode(iris.StatusBadRequest) ctx.StatusCode(iris.StatusBadRequest)
ctx.WriteString(err.Error()) ctx.WriteString(err.Error())
return 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 { type Person struct {
Name string `json:"name"` Name string `json:"name"`
Age int `json:"age"` Age int `json:"age"`
@ -55,9 +57,9 @@ func main() {
"Other": "Something here" "Other": "Something here"
} }
*/ */
// and Content-Type to application/json // and Content-Type to application/json (optionally but good practise)
// //
// The response should be: // The response should be:
// Received: &main.Company{Name:"iris-Go", City:"New York", Other:"Something here"} // Received: main.Company{Name:"iris-Go", City:"New York", Other:"Something here"}
app.Run(iris.Addr(":8080")) app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations)
} }

View File

@ -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:
/*
<person name="Winston Churchill" age="90">
<description>Description of this person, the body of this inner element.</description>
</person>
*/
// 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)
}

View File

@ -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 := `<person name="Winston Churchill" age="90"><description>Description of this person, the body of this inner element.</description></person>`
e.POST("/").WithText(send).Expect().
Status(httptest.StatusOK).Body().Equal(expectedResponse)
}

View File

@ -2,7 +2,6 @@ package main
import ( import (
"bytes" "bytes"
"log"
"github.com/kataras/iris/_examples/http_responsewriter/herotemplate/template" "github.com/kataras/iris/_examples/http_responsewriter/herotemplate/template"
@ -13,11 +12,15 @@ import (
// $ go run app.go // $ go run app.go
// //
// Read more at https://github.com/shiyanhui/hero/hero // Read more at https://github.com/shiyanhui/hero/hero
func main() { func main() {
app := iris.New() app := iris.New()
app.Get("/users", func(ctx iris.Context) { app.Get("/users", func(ctx iris.Context) {
ctx.Gzip(true)
ctx.ContentType("text/html")
var userList = []string{ var userList = []string{
"Alice", "Alice",
"Bob", "Bob",
@ -25,30 +28,27 @@ func main() {
} }
// Had better use buffer sync.Pool. // 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() // buffer := hero.GetBuffer()
// defer hero.PutBuffer(buffer) // defer hero.PutBuffer(buffer)
buffer := new(bytes.Buffer) // buffer := new(bytes.Buffer)
template.UserList(userList, buffer) // template.UserList(userList, buffer)
// ctx.Write(buffer.Bytes())
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",
}
// using an io.Writer for automatic buffer management (i.e. hero built-in buffer pool), // using an io.Writer for automatic buffer management (i.e. hero built-in buffer pool),
// iris context implements the io.Writer by its ResponseWriter // iris context implements the io.Writer by its ResponseWriter
// which is an enhanced version of the standard http.ResponseWriter // which is an enhanced version of the standard http.ResponseWriter
// but still 100% compatible. // but still 100% compatible, GzipResponseWriter too:
template.UserListToWriter(userList, ctx) // _, 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")) app.Run(iris.Addr(":8080"))

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"regexp"
"strconv" "strconv"
"github.com/kataras/iris" "github.com/kataras/iris"
@ -140,6 +141,24 @@ func main() {
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid) 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. }) // 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 // http://localhost:8080/game/a-zA-Z/level/0-9
// remember, alphabetical is lowercase or uppercase letters only. // remember, alphabetical is lowercase or uppercase letters only.
app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) { app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) {

View File

@ -8,7 +8,7 @@ func main() {
app := iris.New() app := iris.New()
none := app.None("/invisible/{username}", func(ctx iris.Context) { 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 != "" { if from := ctx.Values().GetString("from"); from != "" {
ctx.Writef("\nI see that you're coming from %s", from) ctx.Writef("\nI see that you're coming from %s", from)

View File

@ -58,15 +58,11 @@ func (v *pageView) increment() {
} }
func (v *pageView) decrement() { func (v *pageView) decrement() {
oldCount := v.count atomic.AddUint64(&v.count, ^uint64(0))
if oldCount > 0 {
atomic.StoreUint64(&v.count, oldCount-1)
}
} }
func (v *pageView) getCount() uint64 { func (v *pageView) getCount() uint64 {
val := atomic.LoadUint64(&v.count) return atomic.LoadUint64(&v.count)
return val
} }
type ( type (

View File

@ -13,7 +13,7 @@ func main() {
return "Greetings " + s + "!" return "Greetings " + s + "!"
}) })
// $ go get -u github.com/jteeuwen/go-bindata/... // $ go get -u github.com/shuLhan/go-bindata/...
// $ go-bindata ./templates/... // $ go-bindata ./templates/...
// $ go build // $ go build
// $ ./embedding-templates-into-app // $ ./embedding-templates-into-app

View File

@ -30,7 +30,7 @@ func TestConfigurationStatic(t *testing.T) {
afterNew = *app.config 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") t.Fatalf("Passing a Configuration field as Option fails, expected DisableBodyConsumptionOnUnmarshal to be true but was false")
} }

View File

@ -48,4 +48,8 @@ type Application interface {
// If a handler is not already registered, // If a handler is not already registered,
// then it creates & registers a new trivial handler on the-fly. // then it creates & registers a new trivial handler on the-fly.
FireErrorCode(ctx Context) 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
} }

View File

@ -15,7 +15,6 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"reflect"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -49,20 +48,24 @@ type (
// //
// Note: This is totally optionally, the default decoders // Note: This is totally optionally, the default decoders
// for ReadJSON is the encoding/json and for ReadXML is the encoding/xml. // 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 { BodyDecoder interface {
Decode(data []byte) error Decode(data []byte) error
} }
// Unmarshaler is the interface implemented by types that can unmarshal any raw data // 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. // TIP INFO: Any pointer to a value which implements the BodyDecoder can be override the unmarshaler.
Unmarshaler interface { Unmarshaler interface {
Unmarshal(data []byte, v interface{}) error Unmarshal(data []byte, outPtr interface{}) error
} }
// UnmarshalerFunc a shortcut for the Unmarshaler interface // UnmarshalerFunc a shortcut for the Unmarshaler interface
// //
// See 'Unmarshaler' and 'BodyDecoder' for more. // 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. // 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. // Note: Custom context should override this method in order to be able to pass its own context.Context implementation.
Next() 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. // Use .Skip() to skip this handler if needed to execute the next of this returning handler.
NextHandler() 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 // 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. // `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) FormFile(key string) (multipart.File, *multipart.FileHeader, error)
// UploadFormFiles uploads any received file(s) from the client // UploadFormFiles uploads any received file(s) from the client
// to the system physical location "destDirectory". // 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. // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
// //
// See `FormFile` to a more controlled to receive a file. // 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) 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. // should be called before reading the request body from the client.
SetMaxRequestBodySize(limitOverBytes int64) 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. // 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. // Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go
ReadJSON(jsonObject interface{}) error UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) error
// ReadXML reads XML from request's body and binds it to a value of any xml-valid type. // ReadJSON reads JSON from request's body and binds it to a pointer of a value of any json-valid type.
ReadXML(xmlObject interface{}) error //
// 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 // ReadForm binds the formObject with the form data
// it supports any kind of struct. // 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 | // | Body (raw) Writers |
@ -880,7 +910,7 @@ type Context interface {
// TransactionsSkipped returns true if the transactions skipped or canceled at all. // TransactionsSkipped returns true if the transactions skipped or canceled at all.
TransactionsSkipped() bool 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 // based on this context but with a changed method and path
// like it was requested by the user, but it is not. // 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. // 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. // 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. // Application returns the iris app instance which belongs to this context.
// Worth to notice that this function returns an interface // Worth to notice that this function returns an interface
@ -1260,7 +1294,39 @@ func (ctx *context) Next() { // or context.Next(ctx)
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. // Use .Skip() to skip this handler if needed to execute the next of this returning handler.
func (ctx *context) NextHandler() 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 // 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. // `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) { func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
// we don't have access to see if the request is body stream // we don't have access to see if the request is body stream
// and then the ParseMultipartForm can be useless // 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. // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
// //
// See `FormFile` to a more controlled to receive a file. // 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) { func (ctx *context) UploadFormFiles(destDirectory string, before ...func(Context, *multipart.FileHeader)) (n int64, err error) {
err = ctx.request.ParseMultipartForm(ctx.Application().ConfigurationReadOnly().GetPostMaxMemory()) err = ctx.request.ParseMultipartForm(ctx.Application().ConfigurationReadOnly().GetPostMaxMemory())
if err != nil { 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 // UnmarshalBody reads the request's body and binds it to a value or pointer of any type
// Examples of usage: context.ReadJSON, context.ReadXML. // 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 { if ctx.request.Body == nil {
return errors.New("unmarshal: empty body") 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, // in this case the v should be a pointer also,
// but this is up to the user's custom Decode implementation* // but this is up to the user's custom Decode implementation*
// //
// See 'BodyDecoder' for more // See 'BodyDecoder' for more.
if decoder, isDecoder := v.(BodyDecoder); isDecoder { if decoder, isDecoder := outPtr.(BodyDecoder); isDecoder {
return decoder.Decode(rawData) return decoder.Decode(rawData)
} }
// check if v is already a pointer, if yes then pass as it's // // check if v is already a pointer, if yes then pass as it's
if reflect.TypeOf(v).Kind() == reflect.Ptr { // if reflect.TypeOf(v).Kind() == reflect.Ptr {
return unmarshaler.Unmarshal(rawData, v) // return unmarshaler.Unmarshal(rawData, v)
} // } <- no need for that, ReadJSON is documented enough to receive a pointer,
// finally, if the v doesn't contains a self-body decoder and it's not a pointer // we don't need to reduce the performance here by using the reflect.TypeOf method.
// use the custom unmarshaler to bind the body
return unmarshaler.Unmarshal(rawData, &v) // 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 { 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. // 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 { func (ctx *context) ReadJSON(jsonObject interface{}) error {
var unmarshaler = json.Unmarshal var unmarshaler = json.Unmarshal
if ctx.shouldOptimize() { 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. // 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 { func (ctx *context) ReadXML(xmlObject interface{}) error {
return ctx.UnmarshalBody(xmlObject, UnmarshalerFunc(xml.Unmarshal)) return ctx.UnmarshalBody(xmlObject, UnmarshalerFunc(xml.Unmarshal))
} }
@ -2066,6 +2143,8 @@ var (
// ReadForm binds the formObject with the form data // ReadForm binds the formObject with the form data
// it supports any kind of struct. // 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 { func (ctx *context) ReadForm(formObject interface{}) error {
values := ctx.FormValues() values := ctx.FormValues()
if values == nil { if values == nil {
@ -3086,7 +3165,10 @@ func (ctx *context) TransactionsSkipped() bool {
// //
// It's for extreme use cases, 99% of the times will never be useful for you. // It's for extreme use cases, 99% of the times will never be useful for you.
func (ctx *context) Exec(method string, path string) { func (ctx *context) Exec(method string, path string) {
if path != "" { if path == "" {
return
}
if method == "" { if method == "" {
method = "GET" method = "GET"
} }
@ -3097,10 +3179,9 @@ func (ctx *context) Exec(method string, path string) {
// backup the request path information // backup the request path information
backupPath := ctx.Path() backupPath := ctx.Path()
bakcupMethod := ctx.Method() backupMethod := ctx.Method()
// don't backupValues := ctx.Values().ReadOnly() // don't backupValues := ctx.Values().ReadOnly()
// [sessions stays]
// [values stays] // [values stays]
// reset handlers // reset handlers
ctx.SetHandlers(nil) ctx.SetHandlers(nil)
@ -3110,6 +3191,8 @@ func (ctx *context) Exec(method string, path string) {
req.RequestURI = path req.RequestURI = path
req.URL.Path = path req.URL.Path = path
req.Method = method req.Method = method
req.Host = req.Host
// execute the route from the (internal) context router // execute the route from the (internal) context router
// this way we keep the sessions and the values // this way we keep the sessions and the values
ctx.Application().ServeHTTPC(ctx) ctx.Application().ServeHTTPC(ctx)
@ -3120,7 +3203,7 @@ func (ctx *context) Exec(method string, path string) {
// set the request back to its previous state // set the request back to its previous state
req.RequestURI = backupPath req.RequestURI = backupPath
req.URL.Path = backupPath req.URL.Path = backupPath
req.Method = bakcupMethod req.Method = backupMethod
// don't fill the values in order to be able to communicate from and to. // don't fill the values in order to be able to communicate from and to.
// // fill the values as they were before // // fill the values as they were before
@ -3128,6 +3211,11 @@ func (ctx *context) Exec(method string, path string) {
// ctx.Values().Set(key, value) // 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. // Application returns the iris app instance which belongs to this context.

View File

@ -13,7 +13,7 @@ import (
const ( const (
// Version is the string representation of the current local Iris Web Framework version. // 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 // CheckForUpdates checks for any available updates

View File

@ -180,7 +180,7 @@ func (e Entry) BoolDefault(def bool) (bool, error) {
// respects the immutable. // respects the immutable.
func (e Entry) Value() interface{} { func (e Entry) Value() interface{} {
if e.immutable { 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)) vv := reflect.Indirect(reflect.ValueOf(e.ValueRaw))
// return copy of that slice // return copy of that slice

View File

@ -42,6 +42,12 @@ type repository struct {
} }
func (r *repository) register(route *Route) { 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) r.routes = append(r.routes, route)
} }
@ -92,10 +98,15 @@ type APIBuilder struct {
doneGlobalHandlers context.Handlers doneGlobalHandlers context.Handlers
// the per-party // the per-party
relativePath string 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 _ Party = (*APIBuilder)(nil)
var _ RoutesProvider = &APIBuilder{} // passed to the default request handler (routerHandler) var _ RoutesProvider = (*APIBuilder)(nil) // passed to the default request handler (routerHandler)
// NewAPIBuilder creates & returns a new builder // NewAPIBuilder creates & returns a new builder
// which is responsible to build the API and the router handler. // which is responsible to build the API and the router handler.
@ -129,6 +140,16 @@ func (api *APIBuilder) GetReporter() *errors.Reporter {
return api.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. // 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. // 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 // here we separate the subdomain and relative path
subdomain, path := splitSubdomainAndPath(fullpath) subdomain, path := splitSubdomainAndPath(fullpath)
r, err := NewRoute(method, subdomain, path, possibleMainHandlerName, routeHandlers, api.macros) // 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: if err != nil { // template path parser errors:
api.reporter.Add("%v -> %s:%s:%s", err, method, subdomain, path) api.reporter.Add("%v -> %s:%s:%s", err, method, subdomain, path)
return nil return nil // fail on first error.
} }
// Add UseGlobal & DoneGlobal Handlers // Add UseGlobal & DoneGlobal Handlers
r.use(api.beginGlobalHandlers) route.use(api.beginGlobalHandlers)
r.done(api.doneGlobalHandlers) route.done(api.doneGlobalHandlers)
// global // global
api.routes.register(r) api.routes.register(route)
}
// per -party, used for done handlers return route
// api.apiRoutes = append(api.apiRoutes, r)
return r
} }
// HandleMany works like `Handle` but can receive more than one // 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 // append the parent's + child's handlers
middleware := joinHandlers(api.middleware, 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{ return &APIBuilder{
// global/api builder // global/api builder
macros: api.macros, macros: api.macros,
@ -269,8 +301,9 @@ func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) P
reporter: api.reporter, reporter: api.reporter,
// per-party/children // per-party/children
middleware: middleware, middleware: middleware,
doneHandlers: api.doneHandlers, doneHandlers: api.doneHandlers[0:],
relativePath: fullpath, relativePath: fullpath,
allowMethods: allowMethods,
} }
} }

View File

@ -22,6 +22,8 @@ type RequestHandler interface {
HandleRequest(context.Context) HandleRequest(context.Context)
// Build should builds the handler, it's being called on router's BuildRouter. // Build should builds the handler, it's being called on router's BuildRouter.
Build(provider RoutesProvider) error Build(provider RoutesProvider) error
// RouteExists reports whether a particular route exists.
RouteExists(ctx context.Context, method, path string) bool
} }
type tree struct { type tree struct {
@ -160,6 +162,14 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
r.URL.Path = path r.URL.Path = path
url := r.URL.String() 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) ctx.Redirect(url, http.StatusMovedPermanently)
// RFC2616 recommends that a short note "SHOULD" be included in the // 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) 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
}

View File

@ -6,9 +6,6 @@ import (
"github.com/kataras/iris/core/router/macro" "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 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. // 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. // Returns this Party.
Reset() 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. // 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. // if empty method is passed then handler(s) are being registered to all methods, same as .Any.
// //

View File

@ -7,6 +7,7 @@ import (
"strings" "strings"
"github.com/kataras/iris/core/netutil" "github.com/kataras/iris/core/netutil"
"github.com/kataras/iris/core/router/macro/interpreter/lexer"
) )
const ( const (
@ -73,30 +74,91 @@ func joinPath(path1 string, path2 string) string {
// iteratively until no further processing can be done: // iteratively until no further processing can be done:
// //
// 1. Replace multiple slashes with a single slash. // 1. Replace multiple slashes with a single slash.
// 3. Eliminate each inner .. path name element (the parent directory) // 2. Replace '\' with '/'
// along with the non-.. element that precedes it. // 3. Replace "\\" with '/'
// 4. Eliminate .. elements that begin a rooted path: // 4. Ignore anything inside '{' and '}'
// that is, replace "/.." by "/" at the beginning of a path. // 5. Makes sure that prefixed with '/'
// 6. Remove trailing '/'.
// //
// The returned path ends in a slash only if it is the root "/". // The returned path ends in a slash only if it is the root "/".
func cleanPath(s string) string { func cleanPath(s string) string {
// note that we don't care about the performance here, it's before the server ran.
if s == "" || s == "." { if s == "" || s == "." {
return "/" return "/"
} }
// remove suffix "/" // remove suffix "/".
if lidx := len(s) - 1; s[lidx] == '/' { if lidx := len(s) - 1; s[lidx] == '/' {
s = s[:lidx] s = s[:lidx]
} }
// prefix with "/" // prefix with "/".
s = prefix(s, "/") s = prefix(s, "/")
// remove the os specific dir sep // If you're learning go through Iris I will ask you to ignore the
s = strings.Replace(s, "\\", "/", -1) // 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 for {
s = path.Clean(s) 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 return s
} }

View File

@ -4,6 +4,38 @@ import (
"testing" "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) { func TestSplitPath(t *testing.T) {
tests := []struct { tests := []struct {
path string path string
@ -50,8 +82,8 @@ func TestSplitSubdomainAndPath(t *testing.T) {
}{ }{
{"admin./users/42", "admin.", "/users/42"}, {"admin./users/42", "admin.", "/users/42"},
{"//api/users\\42", "", "/api/users/42"}, {"//api/users\\42", "", "/api/users/42"},
{"admin./users/\\42", "admin.", "/users/42"}, {"admin./users//42", "admin.", "/users/42"},
{"*./users/\\42", "*.", "/users/42"}, {"*./users/42/", "*.", "/users/42"},
} }
for i, tt := range tests { for i, tt := range tests {

View File

@ -147,6 +147,12 @@ func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
router.mainHandler(w, r) 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 { type wrapper struct {
router http.HandlerFunc // http.HandlerFunc to catch the CURRENT state of its .ServeHTTP on case of future change. 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) wrapperFunc func(http.ResponseWriter, *http.Request, http.HandlerFunc)

View File

@ -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)
}

6
doc.go
View File

@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
Current Version Current Version
10.0.0 10.3.0
Installation 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, go-bindata gives you two functions, asset and assetNames,
these can be setted to each of the template engines using the `.Binary` func. these can be setted to each of the template engines using the `.Binary` func.
@ -1133,7 +1133,7 @@ Example code:
func main() { func main() {
app := iris.New() app := iris.New()
// $ go get -u github.com/jteeuwen/go-bindata/... // $ go get -u github.com/shuLhan/go-bindata/...
// $ go-bindata ./templates/... // $ go-bindata ./templates/...
// $ go build // $ go build
// $ ./embedding-templates-into-app // $ ./embedding-templates-into-app

10
go19.go
View File

@ -17,6 +17,16 @@ type (
// Developers send responses to the client's request through a Context. // Developers send responses to the client's request through a Context.
// Developers get request information from the client's request by a Context. // Developers get request information from the client's request by a Context.
Context = context.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. // A Handler responds to an HTTP request.
// It writes reply headers and data to the Context.ResponseWriter() and then return. // It writes reply headers and data to the Context.ResponseWriter() and then return.
// Returning signals that the request is finished; // Returning signals that the request is finished;

View File

@ -128,11 +128,10 @@ func (r *Service) GetBytes(key string) ([]byte, error) {
func (r *Service) Delete(key string) error { func (r *Service) Delete(key string) error {
c := r.pool.Get() c := r.pool.Get()
defer c.Close() defer c.Close()
if _, err := c.Do("DEL", r.Config.Prefix+key); err != nil {
_, err := c.Do("DEL", r.Config.Prefix+key)
return err return err
} }
return nil
}
func dial(network string, addr string, pass string) (redis.Conn, error) { func dial(network string, addr string, pass string) (redis.Conn, error) {
if network == "" { if network == "" {

View File

@ -112,7 +112,7 @@ func hi(ctx iris.Context) {
## Embedded ## 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`, `go-bindata` gives you two functions, `Assset` and `AssetNames`,
these can be setted to each of the template engines using the `.Binary` function. 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() { func main() {
app := iris.New() app := iris.New()
// $ go get -u github.com/jteeuwen/go-bindata/... // $ go get -u github.com/shuLhan/go-bindata/...
// $ go-bindata ./templates/... // $ go-bindata ./templates/...
// $ go build // $ go build
// $ ./embedding-templates-into-app // $ ./embedding-templates-into-app

View File

@ -44,6 +44,18 @@ type (
TagParser func(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) 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. // GetValue returns the `Value` as *pongo2.Value type.
// This method was added by balthild at https://github.com/kataras/iris/pull/765 // This method was added by balthild at https://github.com/kataras/iris/pull/765
func (value *Value) GetValue() *pongo2.Value { func (value *Value) GetValue() *pongo2.Value {

View File

@ -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) // 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 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() { if info == nil || info.IsDir() {
return nil return nil
} }

View File

@ -147,6 +147,9 @@ func New(cfg Config) *Server {
func (s *Server) Handler() context.Handler { func (s *Server) Handler() context.Handler {
return func(ctx context.Context) { return func(ctx context.Context) {
c := s.Upgrade(ctx) c := s.Upgrade(ctx)
if c.Err() != nil {
return
}
// NOTE TO ME: fire these first BEFORE startReader and startPinger // NOTE TO ME: fire these first BEFORE startReader and startPinger
// in order to set the events and any messages to send // in order to set the events and any messages to send
// the startPinger will send the OK to the client and only // the startPinger will send the OK to the client and only