new simple _examples/README.md, wiki should live only inside kataras/iris/wiki and the provided e-book

Former-commit-id: 350eafb0f70f8433e394e103ff93fa332ee00a05
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-05-05 16:03:19 +03:00
parent f5e59c10e1
commit c10dd32ad7
28 changed files with 416 additions and 1166 deletions

View File

@ -1,370 +1,195 @@
# Examples # Table of Contents
Please do learn how [net/http](https://golang.org/pkg/net/http/) std package works, first. * Tutorials
* [Caddy](tutorial/caddy)
This folder provides easy to understand code snippets on how to get started with [iris](https://github.com/kataras/iris) web framework. * [MongoDB](tutorial/mongodb)
* [Dropzone.js](tutorial/dropzonejs)
It doesn't always contain the "best ways" but it does cover each important feature that will make you so excited to GO with iris! * [URL Shortener](tutorial/url-shortener/main.go)
* [Online Visitors](tutorial/online-visitors/main.go)
## Running the examples * [REST API for Apache Kafka](tutorial/api-for-apache-kafka)
* [Vue.js Todo (MVC)](tutorial/vuejs-todo-mvc)
<!-- [![Run in Postman](https://run.pstmn.io/button.svg)](https://www.getpostman.com/collections/16e76a9528aba863e821) --> * [gRPC (MVC)](mvc/grpc-compatible)
* HTTP Listening
1. Install the Go Programming Language, version 1.12+ from https://golang.org/dl. * [HOST:PORT](http-listening/listen-addr/main.go)
2. [Install Iris](https://github.com/kataras/iris/wiki/installation) * [Public Test Domain](http-listening/listen-addr-public/main.go)
3. Install any external packages that required by the examples * [UNIX socket file](http-listening/listen-unix/main.go)
* [TLS](http-listening/listen-tls/main.go)
<details> * [Letsencrypt (Automatic Certifications)](http-listening/listen-letsencrypt/main.go)
<summary>External packages</summary> * [Graceful Shutdown](http-listening/graceful-shutdown/default-notifier/main.go)
* [Notify on shutdown](http-listening/notify-on-shutdown/main.go)
```sh * Custom TCP Listener
cd _examples && go get ./... * [Common net.Listener](http-listening/custom-listener/main.go)
```
</details>
And run each example you wanna see, e.g.
```sh
$ cd $GOPATH/src/github.com/kataras/iris/_examples/overview
$ go run main.go
```
> Test the examples by opening a terminal window and execute: `go test -v ./...`
### Overview
- [Hello world!](hello-world/main.go)
- [Docker](docker/README.md)
- [Hello WebAssembly!](webassembly/basic/main.go)
- [Glimpse](overview/main.go)
- [Tutorial: Online Visitors](tutorial/online-visitors/main.go)
- [Tutorial: A Todo MVC Application using Iris and Vue.js](https://hackernoon.com/a-todo-mvc-application-using-iris-and-vue-js-5019ff870064)
- [Tutorial: URL Shortener using BoltDB](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7)
- [Tutorial: How to turn your Android Device into a fully featured Web Server (**MUST**)](https://twitter.com/ThePracticalDev/status/892022594031017988)
- [POC: Convert the medium-sized project "Parrot" from native to Iris](https://github.com/iris-contrib/parrot)
- [POC: Isomorphic react/hot reloadable/redux/css-modules starter kit](https://github.com/kataras/iris-starter-kit)
- [Tutorial: DropzoneJS Uploader](tutorial/dropzonejs)
- [Tutorial: Caddy](tutorial/caddy)
- [Tutorial:Iris Go Framework + MongoDB](https://medium.com/go-language/iris-go-framework-mongodb-552e349eab9c)
- [Tutorial: API for Apache Kafka](tutorial/api-for-apache-kafka)
### Structuring
Nothing stops you from using your favorite folder structure. Iris is a low level web framework, it has got MVC first-class support but it doesn't limit your folder structure, this is your choice.
Structuring depends on your own needs. We can't tell you how to design your own application for sure but you're free to take a closer look to the examples below; you may find something useful that you can borrow for your app;
- [Bootstrapper](structuring/bootstrap)
- [MVC with Repository and Service layer Overview](structuring/mvc-plus-repository-and-service-layers)
- [Login (MVC with Single Responsibility package)](structuring/login-mvc-single-responsibility-package)
- [Login (MVC with Datamodels, Datasource, Repository and Service layer)](structuring/login-mvc)
### HTTP Listening
- [Common, with address](http-listening/listen-addr/main.go)
* [public domain address](http-listening/listen-addr-public/main.go)
* [omit server errors](http-listening/listen-addr/omit-server-errors/main.go)
- [UNIX socket file](http-listening/listen-unix/main.go)
- [TLS](http-listening/listen-tls/main.go)
- [Letsencrypt (Automatic Certifications)](http-listening/listen-letsencrypt/main.go)
- [Notify on shutdown](http-listening/notify-on-shutdown/main.go)
- Custom TCP Listener
* [common net.Listener](http-listening/custom-listener/main.go)
* [SO_REUSEPORT for unix systems](http-listening/custom-listener/unix-reuseport/main.go) * [SO_REUSEPORT for unix systems](http-listening/custom-listener/unix-reuseport/main.go)
- Custom HTTP Server * Custom HTTP Server
* [Pass a custom Server](http-listening/custom-httpserver/easy-way/main.go)
* [Use Iris as a single http.Handler](http-listening/custom-httpserver/std-way/main.go)
* [Multi Instances](http-listening/custom-httpserver/multi/main.go)
* [HTTP/3 Quic](http-listening/http3-quic) * [HTTP/3 Quic](http-listening/http3-quic)
* [easy way](http-listening/custom-httpserver/easy-way/main.go) * Configuration
* [std way](http-listening/custom-httpserver/std-way/main.go) * [Functional](configuration/functional/main.go)
* [multi server instances](http-listening/custom-httpserver/multi/main.go) * [Configuration Struct](configuration/from-configuration-structure/main.go)
- Graceful Shutdown * [Import from YAML](configuration/from-yaml-file/main.go)
* [using the `RegisterOnInterrupt`](http-listening/graceful-shutdown/default-notifier/main.go) * [Share Configuration across instances](configuration/from-yaml-file/shared-configuration/main.go)
* [using a custom notifier](http-listening/graceful-shutdown/custom-notifier/main.go) * [Import from TOML](configuration/from-toml-file/main.go)
* Routing
### Configuration * [Overview](routing/overview/main.go)
* [Basic](routing/basic/main.go)
- [Functional](configuration/functional/main.go) * [Custom HTTP Errors](routing/http-errors/main.go)
- [From Configuration Struct](configuration/from-configuration-structure/main.go) * [Not Found - Suggest Closest Paths](routing/not-found-suggests/main.go)
- [Import from YAML file](configuration/from-yaml-file/main.go) * [Dynamic Path](routing/dynamic-path/main.go)
* [Share Configuration between multiple instances](configuration/from-yaml-file/shared-configuration/main.go) * [Root Wildcard](routing/dynamic-path/root-wildcard/main.go)
- [Import from TOML file](configuration/from-toml-file/main.go) * [Implement a Parameter Type](routing/macros/main.go)
* Middleware
### Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context * [Per Route](routing/writing-a-middleware/per-route/main.go)
* [Globally](routing/writing-a-middleware/globally/main.go)
* `app.Get("{userid:int min(1)}", myHandler)` * [Route Register Rule](routing/route-register-rule/main.go)
* `app.Post("{asset:path}", myHandler)` * Convert net/http Handlers
* `app.Put("{custom:string regexp([a-z]+)}", myHandler)` * [From func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)](convert-handlers/negroni-like/main.go)
* [From http.Handler or http.HandlerFunc](convert-handlers/nethttp/main.go)
Note: unlike other routers you'd seen, iris' router can handle things like these: * [From func(http.HandlerFunc) http.HandlerFunc](convert-handlers/real-usecase-raven/writing-middleware/main.go)
```go * [Route State](routing/route-state/main.go)
// Matches all GET requests prefixed with "/assets/" * [Reverse Routing](routing/reverse/main.go)
app.Get("/assets/{asset:path}", assetsWildcardHandler) * [Router Wrapper](routing/custom-wrapper/main.go)
* [Custom Router](routing/custom-high-level-router/main.go)
// Matches only GET "/" * Custom Context
app.Get("/", indexHandler) * [Method Overriding](routing/custom-context/method-overriding/main.go)
// Matches only GET "/about" * [New Implementation](routing/custom-context/new-implementation/main.go)
app.Get("/about", aboutHandler) * Subdomains
* [Single](subdomains/single/main.go)
// Matches all GET requests prefixed with "/profile/" * [Multi](subdomains/multi/main.go)
// and followed by a single path part * [Wildcard](subdomains/wildcard/main.go)
app.Get("/profile/{username:string}", userHandler) * [WWW](subdomains/www/main.go)
// Matches only GET "/profile/me" because * [Redirection](subdomains/redirect/main.go)
// it does not conflict with /profile/{username:string} * API Versioning
// or the root wildcard {root:path} * [How it works](https://github.com/kataras/iris/wiki/API-versioning)
app.Get("/profile/me", userHandler) * [Example](versioning/main.go)
* API Documentation
// Matches all GET requests prefixed with /users/ * [yaag](apidoc/yaag/main.go)
// and followed by a number which should be equal or bigger than 1 * Testing
app.Get("/user/{userid:int min(1)}", getUserHandler) * [Example](testing/httptest/main_test.go)
// Matches all requests DELETE prefixed with /users/ * File Server
// and following by a number which should be equal or bigger than 1 * [Favicon](file-server/favicon/main.go)
app.Delete("/user/{userid:int min(1)}", deleteUserHandler) * [Basic](file-server/basic/main.go)
* [Embedding Files Into App Executable File](file-server/embedding-files-into-app/main.go)
// Matches all GET requests except "/", "/about", anything starts with "/assets/" etc... * [Embedding Gziped Files Into App Executable File](file-server/embedding-gziped-files-into-app/main.go)
// because it does not conflict with the rest of the routes. * [Send Files (rate limiter included)](file-server/send-files/main.go)
app.Get("{root:path}", rootWildcardHandler) * Single Page Applications
``` * [Basic SPA](file-server/single-page-application/basic/main.go)
* [Embedded Single Page Application](file-server/single-page-application/embedded-single-page-application/main.go)
Navigate through examples for a better understanding. * [Embedded Single Page Application with other routes](file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go)
* View
- [Overview](routing/overview/main.go) * [Overview](view/overview/main.go)
- [Basic](routing/basic/main.go) * [Hi](view/template_html_0/main.go)
- [Controllers](mvc) * [A simple Layout](view/template_html_1/main.go)
- [Custom HTTP Errors](routing/http-errors/main.go) * [Layouts: `yield` and `render` tmpl funcs](view/template_html_2/main.go)
- [Not Found - Suggest Closest Paths](routing/not-found-suggests/main.go) **NEW** * [The `urlpath` tmpl func](view/template_html_3/main.go)
- [Dynamic Path](routing/dynamic-path/main.go) * [The `url` tmpl func](view/template_html_4/main.go)
* [root level wildcard path](routing/dynamic-path/root-wildcard/main.go) * [Inject Data Between Handlers](view/context-view-data/main.go)
- [Write your own custom parameter types](routing/macros/main.go) * [Embedding Templates Into App Executable File](view/embedding-templates-into-app/main.go)
- [Reverse routing](routing/reverse/main.go) * [Write to a custom `io.Writer`](view/write-to)
- [Custom Router (high-level)](routing/custom-high-level-router/main.go) * [Greeting with Pug (Jade)`](view/template_pug_0)
- [Custom Wrapper](routing/custom-wrapper/main.go) * [Pug (Jade) Actions`](view/template_pug_1)
- Custom Context * [Pug (Jade) Includes`](view/template_pug_2)
* [method overriding](routing/custom-context/method-overriding/main.go) * [Pug (Jade) Extends`](view/template_pug_3)
* [new implementation](routing/custom-context/new-implementation/main.go) * [Jet](/view/template_jet_0)
- [Route State](routing/route-state/main.go) * [Jet Embedded](view/template_jet_1_embedded)
- [Writing a middleware](routing/writing-a-middleware) * [Jet 'urlpath' tmpl func](/view/template_jet_2)
* [per-route](routing/writing-a-middleware/per-route/main.go) * [Jet template funcs from structure](/view/template_jet_3)
* [globally](routing/writing-a-middleware/globally/main.go) * Third-Parties
- [Route Register Rule](routing/route-register-rule/main.go) **NEW** * [Render `valyala/quicktemplate` templates](http_responsewriter/quicktemplate)
* [Render `shiyanhui/hero` templates](http_responsewriter/herotemplate)
### Versioning * Request Body
* [Bind JSON](http_request/read-json/main.go)
- [How it works](https://github.com/kataras/iris/blob/master/versioning/README.md) * * [Struct Validation](http_request/read-json-struct-validation/main.go)
- [Example](versioning/main.go) * [Bind XML](http_request/read-xml/main.go)
* [Bind MsgPack](http_request/read-msgpack/main.go)
### Dependency Injection * [Bind YAML](http_request/read-yaml/main.go)
* [Bind Form](http_request/read-form/main.go)
- [Basic](hero/basic/main.go) * [Bind Query](http_request/read-query/main.go)
- [Overview](hero/overview) * [Bind Body](http_request/read-body/main.go)
- [Sessions](hero/sessions) * [Bind Custom per type](http_request/read-custom-per-type/main.go)
- [Yet another dependency injection example and good practises at general](hero/smart-contract/main.go) * [Bind Custom via Unmarshaler](http_request/read-custom-via-unmarshaler/main.go)
* [Bind Many times](http_request/read-many/main.go)
### MVC * [Upload/Read File](http_request/upload-file/main.go)
* [Upload multiple Files](http_request/upload-files/main.go)
- [Hello world](mvc/hello-world/main.go) * [Extract Referrer](http_request/extract-referer/main.go)
- [Basic](mvc/basic/main.go) * Response Writer
- [Basic: wildcard](mvc/basic/wildcard/main.go) **NEW** * [Content Negotiation](http_responsewriter/content-negotiation)
- [Regexp](mvc/regexp/main.go) * [Text, Markdown, YAML, HTML, JSON, JSONP, Msgpack, XML and Binary](http_responsewriter/write-rest/main.go)
- [Session Controller](mvc/session-controller/main.go) * [Write Gzip](http_responsewriter/write-gzip/main.go)
- [Overview - Plus Repository and Service layers](mvc/overview) * [Stream Writer](http_responsewriter/stream-writer/main.go)
- [Login showcase - Plus Repository and Service layers](mvc/login) * [Transactions](http_responsewriter/transactions/main.go)
- [Singleton](mvc/singleton) * [SSE](http_responsewriter/sse/main.go)
- [Websocket Controller](mvc/websocket) * [SSE (third-party package usage for server sent events)](http_responsewriter/sse-third-party/main.go)
- [Register Middleware](mvc/middleware) * Cache
- [Vue.js Todo MVC](tutorial/vuejs-todo-mvc) * [Simple](cache/simple/main.go)
- [gRPC-compatible controller](mvc/grpc-compatible/main.go) **NEW** * [Client-Side (304)](cache/client-side/main.go)
* Localization and Internationalization
### Subdomains * [i18n](i18n/main.go)
* Sitemaps
- [Single](subdomains/single/main.go) * [Sitemap](sitemap/main.go)
- [Multi](subdomains/multi/main.go) * Authentication
- [Wildcard](subdomains/wildcard/main.go) * [Basic Authentication](authentication/basicauth/main.go)
- [WWW](subdomains/www/main.go) * [OAUth2](authentication/oauth2/main.go)
- [Redirect fast](subdomains/redirect/main.go) * [Request Auth(JWT)](experimental-handlers/jwt/main.go)
* [Manage Permissions](permissions/main.go)
### Convert `http.Handler/HandlerFunc` * Cookies
* [Basic](cookies/basic/main.go)
- [From func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)](convert-handlers/negroni-like/main.go) * [Encode/Decode (with `securecookie`)](cookies/securecookie/main.go)
- [From http.Handler or http.HandlerFunc](convert-handlers/nethttp/main.go) * Sessions
- [From func(http.HandlerFunc) http.HandlerFunc](convert-handlers/real-usecase-raven/writing-middleware/main.go) * [Overview](sessions/overview/main.go)
* [Middleware](sessions/middleware/main.go)
### View * [Secure Cookie](sessions/securecookie/main.go)
* [Flash Messages](sessions/flash-messages/main.go)
- [Overview](view/overview/main.go) * [Databases](sessions/database)
- [Hi](view/template_html_0/main.go)
- [A simple Layout](view/template_html_1/main.go)
- [Layouts: `yield` and `render` tmpl funcs](view/template_html_2/main.go)
- [The `urlpath` tmpl func](view/template_html_3/main.go)
- [The `url` tmpl func](view/template_html_4/main.go)
- [Inject Data Between Handlers](view/context-view-data/main.go)
- [Embedding Templates Into App Executable File](view/embedding-templates-into-app/main.go)
- [Write to a custom `io.Writer`](view/write-to)
- [Greeting with Pug (Jade)`](view/template_pug_0)
- [Pug (Jade) Actions`](view/template_pug_1)
- [Pug (Jade) Includes`](view/template_pug_2)
- [Pug (Jade) Extends`](view/template_pug_3)
- [Jet](/view/template_jet_0)
- [Jet Embedded](view/template_jet_1_embedded)
- [Jet 'urlpath' tmpl func](/view/template_jet_2)
- [Jet template funcs from structure](/view/template_jet_3)
You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [hero templates](https://github.com/shiyanhui/hero/hero) files too, simply by using the `context#ResponseWriter`, take a look at the [http_responsewriter/quicktemplate](http_responsewriter/quicktemplate) and [http_responsewriter/herotemplate](http_responsewriter/herotemplate) examples.
### Localization and Internationalization
- [I18n](i18n/main.go) **NEW**
### Sitemap
- [Sitemap](sitemap/main.go) **NEW**
### Desktop App
- [Using blink package](desktop-app/blink) **NEW**
- [Using lorca package](desktop-app/lorca) **NEW**
- [Using webview package](desktop-app/webview) **NEW**
### Authentication
- [Basic Authentication](authentication/basicauth/main.go)
- [OAUth2](authentication/oauth2/main.go)
- [Request Auth(JWT)](experimental-handlers/jwt/main.go)
- [Sessions](#sessions)
### File Server
- [Favicon](file-server/favicon/main.go)
- [Basic](file-server/basic/main.go)
- [Embedding Files Into App Executable File](file-server/embedding-files-into-app/main.go)
- [Embedding Gziped Files Into App Executable File](file-server/embedding-gziped-files-into-app/main.go)
- [Send/Force-Download Files](file-server/send-files/main.go) **UPDATED**
- Single Page Applications
* [single Page Application](file-server/single-page-application/basic/main.go)
* [embedded Single Page Application](file-server/single-page-application/embedded-single-page-application/main.go)
* [embedded Single Page Application with other routes](file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go)
### How to Read from `context.Request() *http.Request`
- [Read JSON](http_request/read-json/main.go)
* [Struct Validation](http_request/read-json-struct-validation/main.go) **UPDATED**
- [Read XML](http_request/read-xml/main.go)
- [Read MsgPack](http_request/read-msgpack/main.go) **NEW**
- [Read YAML](http_request/read-yaml/main.go)
- [Read Form](http_request/read-form/main.go)
- [Read Query](http_request/read-query/main.go)
- [Read Body](http_request/read-body/main.go) **NEW**
- [Read Custom per type](http_request/read-custom-per-type/main.go)
- [Read Custom via Unmarshaler](http_request/read-custom-via-unmarshaler/main.go)
- [Read Many times](http_request/read-many/main.go)
- [Upload/Read File](http_request/upload-file/main.go)
- [Upload multiple files with an easy way](http_request/upload-files/main.go)
- [Extract referrer from "referer" header or URL query parameter](http_request/extract-referer/main.go)
> The `context.Request()` returns the same *http.Request you already know, these examples show some places where the Context uses this object. Besides that you can use it as you did before iris.
### How to Write to `context.ResponseWriter() http.ResponseWriter`
- [Content Negotiation](http_responsewriter/content-negotiation)
- [Write `valyala/quicktemplate` templates](http_responsewriter/quicktemplate)
- [Write `shiyanhui/hero` templates](http_responsewriter/herotemplate)
- [Text, Markdown, HTML, JSON, JSONP, XML, Binary](http_responsewriter/write-rest/main.go)
- [Write Gzip](http_responsewriter/write-gzip/main.go)
- [Stream Writer](http_responsewriter/stream-writer/main.go)
- [Transactions](http_responsewriter/transactions/main.go)
- [SSE](http_responsewriter/sse/main.go)
- [SSE (third-party package usage for server sent events)](http_responsewriter/sse-third-party/main.go)
> The `context/context#ResponseWriter()` returns an enchament version of a http.ResponseWriter, these examples show some places where the Context uses this object. Besides that you can use it as you did before iris.
### ORM
- [Using xorm(Mysql, MyMysql, Postgres, Tidb, **SQLite**, MsSql, MsSql, Oracle)](orm/xorm/main.go)
- [Using gorm](orm/gorm/main.go)
### Permissions
- [permissions](permissions/main.go) **NEW**
### Miscellaneous
- [Rate Limit](miscellaneous/ratelimit/main.go) **NEW**
- [HTTP Method Override](https://github.com/kataras/iris/blob/master/middleware/methodoverride/methodoverride_test.go)
- [Request Logger](http_request/request-logger/main.go)
* [log requests to a file](http_request/request-logger/request-logger-file/main.go)
- [Recovery](miscellaneous/recover/main.go)
- [Profiling (pprof)](miscellaneous/pprof/main.go)
- [Internal Application File Logger](miscellaneous/file-logger/main.go)
- [Google reCAPTCHA](miscellaneous/recaptcha/main.go)
- [hCaptcha](miscellaneous/hcaptcha/main.go) **NEW**
### Community-based Handlers
- [Casbin wrapper](experimental-handlers/casbin/wrapper/main.go)
- [Casbin middleware](experimental-handlers/casbin/middleware/main.go)
- [Cloudwatch](experimental-handlers/cloudwatch/simple/main.go)
- [CORS](experimental-handlers/cors/simple/main.go)
- [JWT](experimental-handlers/jwt/main.go)
- [Newrelic](experimental-handlers/newrelic/simple/main.go)
- [Prometheus](experimental-handlers/prometheus/simple/main.go)
- [Secure](experimental-handlers/secure/simple/main.go)
- [Tollboothic](experimental-handlers/tollboothic/limit-handler/main.go)
- [Cross-Site Request Forgery Protection](experimental-handlers/csrf/main.go)
#### More
https://github.com/kataras/iris/tree/master/middleware#third-party-handlers
### Automated API Documentation
- [yaag](apidoc/yaag/main.go)
### Testing
The `httptest` package is your way for end-to-end HTTP testing, it uses the httpexpect library created by our friend, [gavv](https://github.com/gavv).
[Example](testing/httptest/main_test.go)
### Caching
iris cache library lives on its own [package](https://github.com/kataras/iris/tree/master/cache).
- [Simple](cache/simple/main.go)
- [Client-Side (304)](cache/client-side/main.go) - part of the iris context core
> You're free to use your own favourite caching package if you'd like so.
### Cookies
- [Basic](cookies/basic/main.go)
- [Encode/Decode (securecookie)](cookies/securecookie/main.go)
### Sessions
iris session manager lives on its own [package](https://github.com/kataras/iris/tree/master/sessions).
- [Overview](sessions/overview/main.go)
- [Middleware](sessions/middleware/main.go)
- [Secure Cookie](sessions/securecookie/main.go)
- [Flash Messages](sessions/flash-messages/main.go)
- [Databases](sessions/database)
* [Badger](sessions/database/badger/main.go) * [Badger](sessions/database/badger/main.go)
* [BoltDB](sessions/database/boltdb/main.go) * [BoltDB](sessions/database/boltdb/main.go)
* [Redis](sessions/database/redis/main.go) * [Redis](sessions/database/redis/main.go)
* Websocket
> You're free to use your own favourite sessions package if you'd like so. * [Basic](websocket/basic)
### Websockets
- [Basic](websocket/basic)
* [Server](websocket/basic/server.go) * [Server](websocket/basic/server.go)
* [Go Client](websocket/basic/go-client/client.go) * [Go Client](websocket/basic/go-client/client.go)
* [Browser Client](websocket/basic/browser/index.html) * [Browser Client](websocket/basic/browser/index.html)
* [Browser NPM Client (browserify)](websocket/basic/browserify/app.js) * [Browser NPM Client (browserify)](websocket/basic/browserify/app.js)
- [Native Messages](websocket/native-messages/main.go) * [Native Messages](websocket/native-messages/main.go)
- [TLS Enabled](websocket/secure/README.md) * [TLS](websocket/secure/README.md)
* Dependency Injection
### Hey, You * [Overview (Movies Service)](ependency-injection/overview/main.go)
* [Basic](dependency-injection/basic/main.go)
Developers should read the [godocs](https://godoc.org/github.com/kataras/iris) and https://docs.iris-go.com for a better understanding. * [Middleware](dependency-injection/basic/middleware/main.go)
* [Sessions](dependency-injection/sessions/main.go)
Psst, I almost forgot; do not forget to [star or watch](https://github.com/kataras/iris/stargazers) the project in order to stay updated with the latest tech trends, it never takes more than a second! * [Smart Contract](dependency-injection/smart-contract/main.go)
* MVC
* [Overview - Repository and Service layers](mvc/overview)
* [Login - Repository and Service layers](mvc/login)
* [Hello world](mvc/hello-world/main.go)
* [Basic](mvc/basic/main.go)
* [Wildcard](mvc/basic/wildcard/main.go)
* [Singleton](mvc/singleton)
* [Regexp](mvc/regexp/main.go)
* [Session Controller](mvc/session-controller/main.go)
* [Websocket Controller](mvc/websocket)
* [Register Middleware](mvc/middleware)
* Object-Relational Mapping
* [Using `go-xorm/xorm` (Mysql, MyMysql, Postgres, Tidb, SQLite, MsSql, MsSql, Oracle)](orm/xorm/main.go)
* [Using `jinzhu/gorm`](orm/gorm/main.go)
* Project Structure
* [Bootstrapper](structuring/bootstrap)
* [MVC with Repository and Service layer Overview](structuring/mvc-plus-repository-and-service-layers)
* [Login (MVC with Single Responsibility package)](structuring/login-mvc-single-responsibility-package)
* [Login (MVC with Datamodels, Datasource, Repository and Service layer)](structuring/login-mvc)
* Desktop Applications
* [The blink package](desktop-app/blink)
* [The lorca package](desktop-app/lorca)
* [The webview package](desktop-app/webview)
* Middlewares (Builtin)
* [Rate Limit](miscellaneous/ratelimit/main.go)
* [HTTP Method Override](https://github.com/kataras/iris/blob/master/middleware/methodoverride/methodoverride_test.go)
* [Request Logger](http_request/request-logger/main.go)
* [Log Requests to a File](http_request/request-logger/request-logger-file/main.go)
* [Recovery](miscellaneous/recover/main.go)
* [Profiling (pprof)](miscellaneous/pprof/main.go)
* [Internal Application File Logger](miscellaneous/file-logger/main.go)
* [Google reCAPTCHA](miscellaneous/recaptcha/main.go)
* [hCaptcha](miscellaneous/hcaptcha/main.go)
* Middlewares [(Community)](https://github.com/iris-contrib/middleware)

View File

@ -1,345 +0,0 @@
# Configuration
All configuration's values have default values, things will work as you expected with `iris.New()`.
Configuration is useless before listen functions, so it should be passed on `Application#Run/2` (second argument(s)).
Iris has a type named `Configurator` which is a `func(*iris.Application)`, any function
which completes this can be passed at `Application#Configure` and/or `Application#Run/2`.
`Application#ConfigurationReadOnly()` returns the configuration values.
`.Run` **by `Configuration` struct**
```go
package main
import (
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<b>Hello!</b>")
})
// [...]
// Good when you want to modify the whole configuration.
app.Listen(":8080", iris.WithConfiguration(iris.Configuration{
DisableStartupLog: false,
DisableInterruptHandler: false,
DisablePathCorrection: false,
EnablePathEscape: false,
FireMethodNotAllowed: false,
DisableBodyConsumptionOnUnmarshal: false,
DisableAutoFireStatusCode: false,
TimeFormat: "Mon, 02 Jan 2006 15:04:05 GMT",
Charset: "utf-8",
}))
}
```
`.Run` **by options**
```go
package main
import (
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<b>Hello!</b>")
})
// [...]
// Good when you want to change some of the configuration's field.
// Prefix: "With", code editors will help you navigate through all
// configuration options without even a glitch to the documentation.
app.Listen(":8080", iris.WithoutStartupLog, iris.WithCharset("utf-8"))
// or before run:
// app.Configure(iris.WithoutStartupLog, iris.WithCharset("utf-8"))
// app.Listen(":8080")
}
```
`.Run` **by TOML config file**
```tml
DisablePathCorrection = false
EnablePathEscape = false
FireMethodNotAllowed = true
DisableBodyConsumptionOnUnmarshal = false
TimeFormat = "Mon, 01 Jan 2006 15:04:05 GMT"
Charset = "utf-8"
[Other]
MyServerName = "iris"
```
```go
package main
import (
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<b>Hello!</b>")
})
// [...]
// Good when you have two configurations, one for development and a different one for production use.
app.Listen(":8080", iris.WithConfiguration(iris.TOML("./configs/iris.tml")))
}
```
`.Run` **by YAML config file**
```yml
DisablePathCorrection: false
EnablePathEscape: false
FireMethodNotAllowed: true
DisableBodyConsumptionOnUnmarshal: true
TimeFormat: Mon, 01 Jan 2006 15:04:05 GMT
Charset: UTF-8
```
```go
package main
import (
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<b>Hello!</b>")
})
// [...]
app.Listen(":8080", iris.WithConfiguration(iris.YAML("./configs/iris.yml")))
}
```
## Builtin Configurators
```go
// WithGlobalConfiguration will load the global yaml configuration file
// from the home directory and it will set/override the whole app's configuration
// to that file's contents. The global configuration file can be modified by user
// and be used by multiple iris instances.
//
// This is useful when we run multiple iris servers that share the same
// configuration, even with custom values at its "Other" field.
//
// Usage: `app.Configure(iris.WithGlobalConfiguration)` or `app.Run([iris.Runner], iris.WithGlobalConfiguration)`.
WithGlobalConfiguration
// variables for configurators don't need any receivers, functions
// for them that need (helps code editors to recognise as variables without parenthesis completion).
// WithoutServerError will cause to ignore the matched "errors"
// from the main application's `Run` function.
//
// Usage:
// err := app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed))
// will return `nil` if the server's error was `http/iris#ErrServerClosed`.
//
// See `Configuration#IgnoreServerErrors []string` too.
//
// Example: https://github.com/kataras/iris/tree/master/_examples/http-listening/listen-addr/omit-server-errors
WithoutServerError(errors ...error) Configurator
// WithoutStartupLog turns off the information send, once, to the terminal when the main server is open.
WithoutStartupLog
// WithoutInterruptHandler disables the automatic graceful server shutdown
// when control/cmd+C pressed.
WithoutInterruptHandle
// WithoutPathCorrection disables the PathCorrection setting.
//
// See `Configuration`.
WithoutPathCorrectio
// WithoutPathCorrectionRedirection disables the PathCorrectionRedirection setting.
//
// See `Configuration`.
WithoutPathCorrectionRedirection
// WithoutBodyConsumptionOnUnmarshal disables BodyConsumptionOnUnmarshal setting.
//
// See `Configuration`.
WithoutBodyConsumptionOnUnmarshal
// WithoutAutoFireStatusCode disables the AutoFireStatusCode setting.
//
// See `Configuration`.
WithoutAutoFireStatusCode
// WithPathEscape enables the PathEscape setting.
//
// See `Configuration`.
WithPathEscape
// WithOptimizations can force the application to optimize for the best performance where is possible.
//
// See `Configuration`.
WithOptimizations
// WithFireMethodNotAllowed enables the FireMethodNotAllowed setting.
//
// See `Configuration`.
WithFireMethodNotAllowed
// WithTimeFormat sets the TimeFormat setting.
//
// See `Configuration`.
WithTimeFormat(timeformat string) Configurator
// WithCharset sets the Charset setting.
//
// See `Configuration`.
WithCharset(charset string) Configurator
// WithPostMaxMemory sets the maximum post data size
// that a client can send to the server, this differs
// from the overral request body size which can be modified
// by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize`.
//
// Defaults to 32MB or 32 << 20 if you prefer.
WithPostMaxMemory(limit int64) Configurator
// WithRemoteAddrHeader enables or adds a new or existing request header name
// that can be used to validate the client's real IP.
//
// By-default no "X-" header is consired safe to be used for retrieving the
// client's IP address, because those headers can manually change by
// the client. But sometimes are useful e.g., when behind a proxy
// you want to enable the "X-Forwarded-For" or when cloudflare
// you want to enable the "CF-Connecting-IP", inneed you
// can allow the `ctx.RemoteAddr()` to use any header
// that the client may sent.
//
// Defaults to an empty map but an example usage is:
// WithRemoteAddrHeader("X-Forwarded-For")
//
// Look `context.RemoteAddr()` for more.
WithRemoteAddrHeader(headerName string) Configurator
// WithoutRemoteAddrHeader disables an existing request header name
// that can be used to validate and parse the client's real IP.
//
//
// Keep note that RemoteAddrHeaders is already defaults to an empty map
// so you don't have to call this Configurator if you didn't
// add allowed headers via configuration or via `WithRemoteAddrHeader` before.
//
// Look `context.RemoteAddr()` for more.
WithoutRemoteAddrHeader(headerName string) Configurator
// WithRemoteAddrPrivateSubnet adds a new private sub-net to be excluded from `context.RemoteAddr`.
// See `WithRemoteAddrHeader` too.
WithRemoteAddrPrivateSubnet(startIP, endIP string) Configurator
// WithOtherValue adds a value based on a key to the Other setting.
//
// See `Configuration.Other`.
WithOtherValue(key string, val interface{}) Configurator
// WithSitemap enables the sitemap generator.
// Use the Route's `SetLastMod`, `SetChangeFreq` and `SetPriority` to modify
// the sitemap's URL child element properties.
//
// It accepts a "startURL" input argument which
// is the prefix for the registered routes that will be included in the sitemap.
//
// If more than 50,000 static routes are registered then sitemaps will be splitted and a sitemap index will be served in
// /sitemap.xml.
//
// If `Application.I18n.Load/LoadAssets` is called then the sitemap will contain translated links for each static route.
//
// If the result does not complete your needs you can take control
// and use the github.com/kataras/sitemap package to generate a customized one instead.
//
// Example: https://github.com/kataras/iris/tree/master/_examples/sitemap.
WithSitemap(startURL string) Configurator
// WithTunneling is the `iris.Configurator` for the `iris.Configuration.Tunneling` field.
// It's used to enable http tunneling for an Iris Application, per registered host
//
// Alternatively use the `iris.WithConfiguration(iris.Configuration{Tunneling: iris.TunnelingConfiguration{ ...}}}`.
WithTunneling
```
## Custom Configurator
With the `Configurator` developers can modularize their applications with ease.
Example Code:
```go
// file counter/counter.go
package counter
import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/core/host"
)
func Configurator(app *iris.Application) {
counterValue := 0
go func() {
ticker := time.NewTicker(time.Second)
for range ticker.C {
counterValue++
}
app.ConfigureHost(func(h *host.Supervisor) { // <- HERE: IMPORTANT
h.RegisterOnShutdown(func() {
ticker.Stop()
})
})
}()
app.Get("/counter", func(ctx iris.Context) {
ctx.Writef("Counter value = %d", counterValue)
})
}
```
```go
// file: main.go
package main
import (
"counter"
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Configure(counter.Configurator)
app.Listen(":8080")
}
```

View File

@ -10,7 +10,7 @@ package datamodels
// as the only one Movie model in our application, // as the only one Movie model in our application,
// for the shake of simplicty. // for the shake of simplicty.
type Movie struct { type Movie struct {
ID int64 `json:"id"` ID uint64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Year int `json:"year"` Year int `json:"year"`
Genre string `json:"genre"` Genre string `json:"genre"`

View File

@ -2,10 +2,10 @@
package datasource package datasource
import "github.com/kataras/iris/v12/_examples/hero/overview/datamodels" import "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels"
// Movies is our imaginary data source. // Movies is our imaginary data source.
var Movies = map[int64]datamodels.Movie{ var Movies = map[uint64]datamodels.Movie{
1: { 1: {
ID: 1, ID: 1,
Name: "Casablanca", Name: "Casablanca",

View File

@ -0,0 +1,55 @@
// file: main.go
package main
import (
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/datasource"
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/repositories"
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/services"
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/web/middleware"
"github.com/kataras/iris/v12/_examples/dependency-injection/overview/web/routes"
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
app.Logger().SetLevel("debug")
// Load the template files.
app.RegisterView(iris.HTML("./web/views", ".html"))
// Create our movie repository with some (memory) data from the datasource.
repo := repositories.NewMovieRepository(datasource.Movies)
app.Party("/hello").ConfigureContainer(func(r *iris.APIContainer) {
r.Get("/", routes.Hello)
r.Get("/{name}", routes.HelloName)
})
app.Party("/movies").ConfigureContainer(func(r *iris.APIContainer) {
// Create our movie service, we will bind it to the movie app's dependencies.
movieService := services.NewMovieService(repo)
r.RegisterDependency(movieService)
// Add the basic authentication(admin:password) middleware
// for the /movies based requests.
r.Use(middleware.BasicAuth)
r.Get("/", routes.Movies)
r.Get("/{id:uint64}", routes.MovieByID)
r.Put("/{id:uint64}", routes.UpdateMovieByID)
r.Delete("/{id:uint64}", routes.DeleteMovieByID)
})
// http://localhost:8080/hello
// http://localhost:8080/hello/iris
// http://localhost:8080/movies ("admin": "password")
// http://localhost:8080/movies/1
app.Listen(
// Start the web server at localhost:8080
"localhost:8080",
// enables faster json serialization and more:
iris.WithOptimizations,
)
}

View File

@ -6,7 +6,7 @@ import (
"errors" "errors"
"sync" "sync"
"github.com/kataras/iris/v12/_examples/hero/overview/datamodels" "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels"
) )
// Query represents the visitor and action queries. // Query represents the visitor and action queries.
@ -27,14 +27,14 @@ type MovieRepository interface {
// NewMovieRepository returns a new movie memory-based repository, // NewMovieRepository returns a new movie memory-based repository,
// the one and only repository type in our example. // the one and only repository type in our example.
func NewMovieRepository(source map[int64]datamodels.Movie) MovieRepository { func NewMovieRepository(source map[uint64]datamodels.Movie) MovieRepository {
return &movieMemoryRepository{source: source} return &movieMemoryRepository{source: source}
} }
// movieMemoryRepository is a "MovieRepository" // movieMemoryRepository is a "MovieRepository"
// which manages the movies using the memory data source (map). // which manages the movies using the memory data source (map).
type movieMemoryRepository struct { type movieMemoryRepository struct {
source map[int64]datamodels.Movie source map[uint64]datamodels.Movie
mu sync.RWMutex mu sync.RWMutex
} }
@ -115,7 +115,7 @@ func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamode
id := movie.ID id := movie.ID
if id == 0 { // Create new action if id == 0 { // Create new action
var lastID int64 var lastID uint64
// find the biggest ID in order to not have duplications // find the biggest ID in order to not have duplications
// in productions apps you can use a third-party // in productions apps you can use a third-party
// library to generate a UUID as string. // library to generate a UUID as string.

View File

@ -3,8 +3,8 @@
package services package services
import ( import (
"github.com/kataras/iris/v12/_examples/hero/overview/datamodels" "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels"
"github.com/kataras/iris/v12/_examples/hero/overview/repositories" "github.com/kataras/iris/v12/_examples/dependency-injection/overview/repositories"
) )
// MovieService handles some of the CRUID operations of the movie datamodel. // MovieService handles some of the CRUID operations of the movie datamodel.
@ -15,9 +15,9 @@ import (
// because we may need to change or try an experimental different domain logic at the future. // because we may need to change or try an experimental different domain logic at the future.
type MovieService interface { type MovieService interface {
GetAll() []datamodels.Movie GetAll() []datamodels.Movie
GetByID(id int64) (datamodels.Movie, bool) GetByID(id uint64) (datamodels.Movie, bool)
DeleteByID(id int64) bool DeleteByID(id uint64) bool
UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error) UpdatePosterAndGenreByID(id uint64, poster string, genre string) (datamodels.Movie, error)
} }
// NewMovieService returns the default movie service. // NewMovieService returns the default movie service.
@ -39,14 +39,14 @@ func (s *movieService) GetAll() []datamodels.Movie {
} }
// GetByID returns a movie based on its id. // GetByID returns a movie based on its id.
func (s *movieService) GetByID(id int64) (datamodels.Movie, bool) { func (s *movieService) GetByID(id uint64) (datamodels.Movie, bool) {
return s.repo.Select(func(m datamodels.Movie) bool { return s.repo.Select(func(m datamodels.Movie) bool {
return m.ID == id return m.ID == id
}) })
} }
// UpdatePosterAndGenreByID updates a movie's poster and genre. // UpdatePosterAndGenreByID updates a movie's poster and genre.
func (s *movieService) UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error) { func (s *movieService) UpdatePosterAndGenreByID(id uint64, poster string, genre string) (datamodels.Movie, error) {
// update the movie and return it. // update the movie and return it.
return s.repo.InsertOrUpdate(datamodels.Movie{ return s.repo.InsertOrUpdate(datamodels.Movie{
ID: id, ID: id,
@ -58,7 +58,7 @@ func (s *movieService) UpdatePosterAndGenreByID(id int64, poster string, genre s
// DeleteByID deletes a movie by its id. // DeleteByID deletes a movie by its id.
// //
// Returns true if deleted otherwise false. // Returns true if deleted otherwise false.
func (s *movieService) DeleteByID(id int64) bool { func (s *movieService) DeleteByID(id uint64) bool {
return s.repo.Delete(func(m datamodels.Movie) bool { return s.repo.Delete(func(m datamodels.Movie) bool {
return m.ID == id return m.ID == id
}, 1) }, 1)

View File

@ -5,8 +5,8 @@ package routes
import ( import (
"errors" "errors"
"github.com/kataras/iris/v12/_examples/hero/overview/datamodels" "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels"
"github.com/kataras/iris/v12/_examples/hero/overview/services" "github.com/kataras/iris/v12/_examples/dependency-injection/overview/services"
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
) )
@ -21,14 +21,14 @@ func Movies(service services.MovieService) (results []datamodels.Movie) {
// MovieByID returns a movie. // MovieByID returns a movie.
// Demo: // Demo:
// curl -i http://localhost:8080/movies/1 // curl -i http://localhost:8080/movies/1
func MovieByID(service services.MovieService, id int64) (movie datamodels.Movie, found bool) { func MovieByID(service services.MovieService, id uint64) (movie datamodels.Movie, found bool) {
return service.GetByID(id) // it will throw 404 if not found. return service.GetByID(id) // it will throw 404 if not found.
} }
// UpdateMovieByID updates a movie. // UpdateMovieByID updates a movie.
// Demo: // Demo:
// curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1 // curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1
func UpdateMovieByID(ctx iris.Context, service services.MovieService, id int64) (datamodels.Movie, error) { func UpdateMovieByID(ctx iris.Context, service services.MovieService, id uint64) (datamodels.Movie, error) {
// get the request data for poster and genre // get the request data for poster and genre
file, info, err := ctx.FormFile("poster") file, info, err := ctx.FormFile("poster")
if err != nil { if err != nil {
@ -47,7 +47,7 @@ func UpdateMovieByID(ctx iris.Context, service services.MovieService, id int64)
// DeleteMovieByID deletes a movie. // DeleteMovieByID deletes a movie.
// Demo: // Demo:
// curl -i -X DELETE -u admin:password http://localhost:8080/movies/1 // curl -i -X DELETE -u admin:password http://localhost:8080/movies/1
func DeleteMovieByID(service services.MovieService, id int64) interface{} { func DeleteMovieByID(service services.MovieService, id uint64) interface{} {
wasDel := service.DeleteByID(id) wasDel := service.DeleteByID(id)
if wasDel { if wasDel {
// return the deleted movie's ID // return the deleted movie's ID

View File

@ -0,0 +1,34 @@
package main
import (
"time"
"github.com/kataras/iris/v12/_examples/dependency-injection/sessions/routes"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/sessions"
)
func main() {
app := iris.New()
sessionManager := sessions.New(sessions.Config{
Cookie: "site_session_id",
Expires: 60 * time.Minute,
AllowReclaim: true,
})
// Session is automatically binded through `sessions.Get(ctx)`
// if a *sessions.Session input argument is present on the handler's function,
// which `routes.Index` does.
app.Use(sessionManager.Handler())
// Method: GET
// Path: http://localhost:8080
app.ConfigureContainer(registerRoutes)
app.Listen(":8080")
}
func registerRoutes(api *iris.APIContainer) {
api.Get("/", routes.Index)
}

View File

@ -0,0 +1,17 @@
package routes
import (
"fmt"
"github.com/kataras/iris/v12/sessions"
)
// Index will increment a simple int version based on the visits that this user/session did.
func Index(session *sessions.Session) string {
// it increments a "visits" value of integer by one,
// if the entry with key 'visits' doesn't exist it will create it for you.
visits := session.Increment("visits", 1)
// write the current, updated visits.
return fmt.Sprintf("%d visit(s) from my current session", visits)
}

View File

@ -5,8 +5,6 @@ import (
"strings" "strings"
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/hero"
// External package to optionally filter JSON responses before sent, // External package to optionally filter JSON responses before sent,
// see `sendJSON` for more. // see `sendJSON` for more.
@ -17,19 +15,9 @@ import (
$ go get github.com/jmespath/go-jmespath $ go get github.com/jmespath/go-jmespath
*/ */
func newApp() *iris.Application {
app := iris.New()
// PartyFunc is the same as usersRouter := app.Party("/users")
// but it gives us an easy way to call router's registration functions,
// i.e functions from another package that can handle this group of routes.
app.PartyFunc("/users", registerUsersRoutes)
return app
}
func main() { func main() {
app := newApp() app := newApp()
app.Logger().SetLevel("debug")
// http://localhost:8080/users?query=[?Name == 'John Doe'].Age // http://localhost:8080/users?query=[?Name == 'John Doe'].Age
// <- client will receive the age of a user which his name is "John Doe". // <- client will receive the age of a user which his name is "John Doe".
@ -45,6 +33,17 @@ func main() {
app.Listen(":8080") app.Listen(":8080")
} }
func newApp() *iris.Application {
app := iris.New()
// PartyFunc is the same as usersRouter := app.Party("/users")
// but it gives us an easy way to call router's registration functions,
// i.e functions from another package that can handle this group of routes.
app.PartyFunc("/users", registerUsersRoutes)
return app
}
/* /*
START OF USERS ROUTER START OF USERS ROUTER
*/ */
@ -52,7 +51,7 @@ func main() {
func registerUsersRoutes(usersRouter iris.Party) { func registerUsersRoutes(usersRouter iris.Party) {
// GET: /users // GET: /users
usersRouter.Get("/", getAllUsersHandler) usersRouter.Get("/", getAllUsersHandler)
usersRouter.PartyFunc("/{name:string}", registerUserRoutes) usersRouter.Party("/{name}").ConfigureContainer(registerUserRoutes)
} }
type user struct { type user struct {
@ -78,16 +77,12 @@ func getAllUsersHandler(ctx iris.Context) {
START OF USERS.USER SUB ROUTER START OF USERS.USER SUB ROUTER
*/ */
func registerUserRoutes(userRouter iris.Party) { func registerUserRoutes(userRouter *iris.APIContainer) {
// create a new dependency injection manager for this sub router. userRouter.RegisterDependency(userDependency)
userDeps := hero.New()
// you can also use the global/package-level hero.Register(userDependency) as we have already learned in other examples.
userDeps.Register(userDependency)
// GET: /users/{name:string} // GET: /users/{name:string}
userRouter.Get("/", userDeps.Handler(getUserHandler)) userRouter.Get("/", getUserHandler)
// GET: /users/{name:string}/age // GET: /users/{name:string}/age
userRouter.Get("/age", userDeps.Handler(getUserAgeHandler)) userRouter.Get("/age", getUserAgeHandler)
} }
var userDependency = func(ctx iris.Context) *user { var userDependency = func(ctx iris.Context) *user {
@ -108,30 +103,12 @@ var userDependency = func(ctx iris.Context) *user {
} }
func getUserHandler(ctx iris.Context, u *user) { func getUserHandler(ctx iris.Context, u *user) {
if u == nil {
return
}
sendJSON(ctx, u) sendJSON(ctx, u)
} }
func getUserAgeHandler(ctx iris.Context, u *user) {
if u == nil {
return
}
ctx.Writef("%d", u.Age)
}
/* Remember, with 'hero' you get mvc-like functions, so this can work too:
func getUserAgeHandler(u *user) string { func getUserAgeHandler(u *user) string {
if u == nil {
return ""
}
return fmt.Sprintf("%d", u.Age) return fmt.Sprintf("%d", u.Age)
} }
*/
/* END OF USERS.USER SUB ROUTER */ /* END OF USERS.USER SUB ROUTER */
@ -158,11 +135,8 @@ func fail(ctx iris.Context, statusCode int, format string, a ...interface{}) {
ctx.Application().Logger().Error(err) ctx.Application().Logger().Error(err)
} }
ctx.StatusCode(statusCode)
ctx.JSON(err)
// no next handlers will run. // no next handlers will run.
ctx.StopExecution() ctx.StopWithJSON(statusCode, err)
} }
// JSON helper to give end-user the ability to put indention chars or filtering the response, you can do that, optionally. // JSON helper to give end-user the ability to put indention chars or filtering the response, you can do that, optionally.
@ -177,6 +151,6 @@ func sendJSON(ctx iris.Context, resp interface{}) (err error) {
} }
} }
_, err = ctx.JSON(resp, context.JSON{Indent: indent, UnescapeHTML: true}) _, err = ctx.JSON(resp, iris.JSON{Indent: indent, UnescapeHTML: true})
return err return err
} }

View File

@ -1,13 +0,0 @@
# hero: basic
## 1. Path Parameters - Builtin Dependencies
![](https://github.com/kataras/explore/raw/master/iris/hero/hero-1-monokai.png)
## 2. Services - Static Dependencies
![](https://github.com/kataras/explore/raw/master/iris/hero/hero-2-monokai.png)
## 3. Per-Request - Dynamic Dependencies
![](https://github.com/kataras/explore/raw/master/iris/hero/hero-3-monokai.png)

View File

@ -1,66 +0,0 @@
package main
import (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/hero"
)
func main() {
app := iris.New()
// 1
helloHandler := hero.Handler(hello)
app.Get("/{to:string}", helloHandler)
// 2
hero.Register(&myTestService{
prefix: "Service: Hello",
})
helloServiceHandler := hero.Handler(helloService)
app.Get("/service/{to:string}", helloServiceHandler)
// 3
hero.Register(func(ctx iris.Context) (form LoginForm) {
// it binds the "form" with a
// x-www-form-urlencoded form data and returns it.
ctx.ReadForm(&form)
return
})
loginHandler := hero.Handler(login)
app.Post("/login", loginHandler)
// http://localhost:8080/your_name
// http://localhost:8080/service/your_name
app.Listen(":8080")
}
func hello(to string) string {
return "Hello " + to
}
type Service interface {
SayHello(to string) string
}
type myTestService struct {
prefix string
}
func (s *myTestService) SayHello(to string) string {
return s.prefix + " " + to
}
func helloService(to string, service Service) string {
return service.SayHello(to)
}
type LoginForm struct {
Username string `form:"username"`
Password string `form:"password"`
}
func login(form LoginForm) string {
return "Hello " + form.Username
}

View File

@ -1,56 +0,0 @@
// file: main.go
package main
import (
"github.com/kataras/iris/v12/_examples/hero/overview/datasource"
"github.com/kataras/iris/v12/_examples/hero/overview/repositories"
"github.com/kataras/iris/v12/_examples/hero/overview/services"
"github.com/kataras/iris/v12/_examples/hero/overview/web/middleware"
"github.com/kataras/iris/v12/_examples/hero/overview/web/routes"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/hero"
)
func main() {
app := iris.New()
app.Logger().SetLevel("debug")
// Load the template files.
app.RegisterView(iris.HTML("./web/views", ".html"))
// Create our movie repository with some (memory) data from the datasource.
repo := repositories.NewMovieRepository(datasource.Movies)
// Create our movie service, we will bind it to the movie app's dependencies.
movieService := services.NewMovieService(repo)
hero.Register(movieService)
// Serve our routes with hero handlers.
app.PartyFunc("/hello", func(r iris.Party) {
r.Get("/", hero.Handler(routes.Hello))
r.Get("/{name}", hero.Handler(routes.HelloName))
})
app.PartyFunc("/movies", func(r iris.Party) {
// Add the basic authentication(admin:password) middleware
// for the /movies based requests.
r.Use(middleware.BasicAuth)
r.Get("/", hero.Handler(routes.Movies))
r.Get("/{id:long}", hero.Handler(routes.MovieByID))
r.Put("/{id:long}", hero.Handler(routes.UpdateMovieByID))
r.Delete("/{id:long}", hero.Handler(routes.DeleteMovieByID))
})
// http://localhost:8080/hello
// http://localhost:8080/hello/iris
// http://localhost:8080/movies
// http://localhost:8080/movies/1
app.Run(
// Start the web server at localhost:8080
iris.Addr("localhost:8080"),
// enables faster json serialization and more:
iris.WithOptimizations,
)
}

View File

@ -1,36 +0,0 @@
package main
import (
"time"
"github.com/kataras/iris/v12/_examples/hero/sessions/routes"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/hero" // <- IMPORTANT
"github.com/kataras/iris/v12/sessions"
)
func main() {
app := iris.New()
sessionManager := sessions.New(sessions.Config{
Cookie: "site_session_id",
Expires: 60 * time.Minute,
AllowReclaim: true,
})
// Register
// dynamic dependencies like the *sessions.Session, from `sessionManager.Start(ctx) *sessions.Session` <- it accepts a Context and it returns
// something -> this is called dynamic request-time dependency and that "something" can be used to your handlers as input parameters,
// no limit about the number of dependencies, each handler will be builded once, before the server ran and it will use only dependencies that it needs.
hero.Register(sessionManager.Start)
// convert any function to an iris Handler, their input parameters are being resolved using the unique Iris' blazing-fast dependency injection
// for services or dynamic dependencies like the *sessions.Session, from sessionManager.Start(ctx) *sessions.Session) <- it accepts a context and it returns
// something-> this is called dynamic request-time dependency.
indexHandler := hero.Handler(routes.Index)
// Method: GET
// Path: http://localhost:8080
app.Get("/", indexHandler)
app.Listen(":8080")
}

View File

@ -1,26 +0,0 @@
package routes
import (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/sessions"
)
// Index will increment a simple int version based on the visits that this user/session did.
func Index(ctx iris.Context, session *sessions.Session) {
// it increments a "visits" value of integer by one,
// if the entry with key 'visits' doesn't exist it will create it for you.
visits := session.Increment("visits", 1)
// write the current, updated visits.
ctx.Writef("%d visit(s) from my current session", visits)
}
/*
You can also do anything that an MVC function can, i.e:
func Index(ctx iris.Context,session *sessions.Session) string {
visits := session.Increment("visits", 1)
return fmt.Spritnf("%d visit(s) from my current session", visits)
}
// you can also omit iris.Context input parameter and use dependency injection for LoginForm and etc. <- look the mvc examples.
*/

View File

@ -8,10 +8,8 @@ import (
func main() { func main() {
app := iris.New() app := iris.New()
// subdomains works with all available routers, like other features too. // Subdomain method is just another Party.
admin := app.Subdomain("admin")
// no order, you can register subdomains at the end also.
admin := app.Party("admin.")
{ {
// admin.mydomain.com // admin.mydomain.com
admin.Get("/", func(c iris.Context) { admin.Get("/", func(c iris.Context) {
@ -27,7 +25,7 @@ func main() {
}) })
} }
// mydomain.com/ // mydomain.com
app.Get("/", func(c iris.Context) { app.Get("/", func(c iris.Context) {
c.Writef("INDEX FROM no-subdomain hey") c.Writef("INDEX FROM no-subdomain hey")
}) })

View File

@ -498,14 +498,15 @@ And finally our main application's endpoint.
package main package main
import ( import (
"vuejs-todo-mvc/todo" "strings"
"vuejs-todo-mvc/web/controllers"
"github.com/kataras/iris/v12/_examples/tutorial/vuejs-todo-mvc/src/todo"
"github.com/kataras/iris/v12/_examples/tutorial/vuejs-todo-mvc/src/web/controllers"
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/mvc"
"github.com/kataras/iris/v12/sessions" "github.com/kataras/iris/v12/sessions"
"github.com/kataras/iris/v12/websocket" "github.com/kataras/iris/v12/websocket"
"github.com/kataras/iris/v12/mvc"
) )
func main() { func main() {
@ -523,16 +524,8 @@ func main() {
Cookie: "iris_session", Cookie: "iris_session",
}) })
// configure the websocket server. // create a sub router and register the http controllers.
ws := websocket.New(websocket.Config{})
// create a sub router and register the client-side library for the iris websockets,
// you could skip it but iris websockets supports socket.io-like API.
todosRouter := app.Party("/todos") todosRouter := app.Party("/todos")
// http://localhost:8080/todos/iris-ws.js
// serve the javascript client library to communicate with
// the iris high level websocket event system.
todosRouter.Any("/iris-ws.js", websocket.ClientHandler())
// create our mvc application targeted to /todos relative sub path. // create our mvc application targeted to /todos relative sub path.
todosApp := mvc.New(todosRouter) todosApp := mvc.New(todosRouter)
@ -540,12 +533,27 @@ func main() {
// any dependencies bindings here... // any dependencies bindings here...
todosApp.Register( todosApp.Register(
todo.NewMemoryService(), todo.NewMemoryService(),
sess.Start,
ws.Upgrade,
) )
todosController := new(controllers.TodoController)
// controllers registration here... // controllers registration here...
todosApp.Handle(new(controllers.TodoController)) todosApp.Handle(todosController)
// Create a sub mvc app for websocket controller.
// Inherit the parent's dependencies.
todosWebsocketApp := todosApp.Party("/sync")
todosWebsocketApp.HandleWebsocket(todosController).
SetNamespace("todos").
SetEventMatcher(func(methodName string) (string, bool) {
return strings.ToLower(methodName), true
})
websocketServer := websocket.New(websocket.DefaultGorillaUpgrader, todosWebsocketApp)
idGenerator := func(ctx iris.Context) string {
id := sess.Start(ctx).ID()
return id
}
todosWebsocketApp.Router.Get("/", websocket.Handler(websocketServer, idGenerator))
// start the web server at http://localhost:8080 // start the web server at http://localhost:8080
app.Listen(":8080") app.Listen(":8080")

View File

@ -36,7 +36,6 @@ func main() {
// any dependencies bindings here... // any dependencies bindings here...
todosApp.Register( todosApp.Register(
todo.NewMemoryService(), todo.NewMemoryService(),
sess.Start,
) )
todosController := new(controllers.TodoController) todosController := new(controllers.TodoController)

View File

@ -26,6 +26,14 @@ func (api *APIContainer) Party(relativePath string, handlersFn ...interface{}) *
return p.ConfigureContainer() return p.ConfigureContainer()
} }
// PartyFunc same as `Party` but it accepts a party builder function instead.
// Returns the new Party's APIContainer
func (api *APIContainer) PartyFunc(relativePath string, fn func(*APIContainer)) *APIContainer {
childContainer := api.Party(relativePath)
fn(childContainer)
return childContainer
}
// OnError adds an error handler for this Party's DI Hero Container and its handlers (or controllers). // OnError adds an error handler for this Party's DI Hero Container and its handlers (or controllers).
// The "errorHandler" handles any error may occurred and returned // The "errorHandler" handles any error may occurred and returned
// during dependencies injection of the Party's hero handlers or from the handlers themselves. // during dependencies injection of the Party's hero handlers or from the handlers themselves.
@ -89,6 +97,14 @@ func (api *APIContainer) convertHandlerFuncs(relativePath string, handlersFn ...
return handlers return handlers
} }
// Handler receives a function which can receive dependencies and output result
// and returns a common Iris Handler, useful for Versioning API integration otherwise
// the `Handle/Get/Post...` methods are preferable.
func (api *APIContainer) Handler(handlerFn interface{}, handlerParamsCount int) context.Handler {
paramsCount := macro.CountParams(api.Self.GetRelPath(), *api.Self.Macros()) + handlerParamsCount
return api.Container.HandlerWithParams(handlerFn, paramsCount)
}
// Use same as `Self.Use` but it accepts dynamic functions as its "handlersFn" input. // Use same as `Self.Use` but it accepts dynamic functions as its "handlersFn" input.
// //
// See `OnError`, `RegisterDependency`, `Done` and `Handle` for more. // See `OnError`, `RegisterDependency`, `Done` and `Handle` for more.

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"path/filepath"
"strings" "strings"
"time" "time"
@ -342,7 +343,7 @@ func (r *Route) ResolvePath(args ...string) string {
} }
func traceHandlerFile(method, name, line string, number int) string { func traceHandlerFile(method, name, line string, number int) string {
file := fmt.Sprintf("(%s:%d)", line, number) file := fmt.Sprintf("(%s:%d)", filepath.ToSlash(line), number)
if context.IgnoreHandlerName(name) { if context.IgnoreHandlerName(name) {
return "" return ""

View File

@ -54,7 +54,7 @@ func defaultResultHandler(ctx context.Context, v interface{}) error {
// All types that complete this interface // All types that complete this interface
// can be returned as values from the method functions. // can be returned as values from the method functions.
// //
// Example at: https://github.com/kataras/iris/tree/master/_examples/hero/overview. // Example at: https://github.com/kataras/iris/tree/master/_examples/dependency-injection/overview.
type Result interface { type Result interface {
// Dispatch should send a response to the client. // Dispatch should send a response to the client.
Dispatch(context.Context) Dispatch(context.Context)
@ -436,7 +436,7 @@ func (r Response) Dispatch(ctx context.Context) {
// wraps the template file name, layout, (any) view data, status code and error. // wraps the template file name, layout, (any) view data, status code and error.
// It's smart enough to complete the request and send the correct response to the client. // It's smart enough to complete the request and send the correct response to the client.
// //
// Example at: https://github.com/kataras/iris/blob/master/_examples/hero/overview/web/routes/hello.go. // Example at: https://github.com/kataras/iris/blob/master/_examples/dependency-injection/overview/web/routes/hello.go.
type View struct { type View struct {
Name string Name string
Layout string Layout string

View File

@ -103,6 +103,13 @@ func makeHandler(fn interface{}, c *Container, paramsCount int) context.Handler
return return
} }
// If ~an error status code is set or~ execution has stopped
// from within the dependency (something went wrong while validating the request),
// then stop everything and let handler fire that status code.
if ctx.IsStopped() /* || context.StatusCodeNotSuccessful(ctx.GetStatusCode())*/ {
return
}
inputs[binding.Input.Index] = input inputs[binding.Input.Index] = input
} }

View File

@ -1,142 +0,0 @@
# Versioning
The [versioning](https://github.com/kataras/iris/tree/master/versioning) package provides [semver](https://semver.org/) versioning for your APIs. It implements all the suggestions written at [api-guidelines](https://github.com/byrondover/api-guidelines/blob/master/Guidelines.md#versioning) and more.
The version comparison is done by the [go-version](https://github.com/hashicorp/go-version) package. It supports matching over patterns like `">= 1.0, < 3"` and etc.
## Features
- per route version matching, a normal iris handler with "switch" cases via Map for version => handler
- per group versioned routes and deprecation API
- version matching like ">= 1.0, < 2.0" or just "2.0.1" and etc.
- version not found handler (can be customized by simply adding the versioning.NotFound: customNotMatchVersionHandler on the Map)
- version is retrieved from the "Accept" and "Accept-Version" headers (can be customized via middleware)
- respond with "X-API-Version" header, if version found.
- deprecation options with customizable "X-API-Warn", "X-API-Deprecation-Date", "X-API-Deprecation-Info" headers via `Deprecated` wrapper.
## Get version
Current request version is retrieved by `versioning.GetVersion(ctx)`.
By default the `GetVersion` will try to read from:
- `Accept` header, i.e `Accept: "application/json; version=1.0"`
- `Accept-Version` header, i.e `Accept-Version: "1.0"`
You can also set a custom version for a handler via a middleware by using the context's store values.
For example:
```go
func(ctx iris.Context) {
ctx.Values().Set(versioning.Key, ctx.URLParamDefault("version", "1.0"))
ctx.Next()
}
```
## Match version to handler
The `versioning.NewMatcher(versioning.Map) iris.Handler` creates a single handler which decides what handler need to be executed based on the requested version.
```go
app := iris.New()
// middleware for all versions.
myMiddleware := func(ctx iris.Context) {
// [...]
ctx.Next()
}
myCustomNotVersionFound := func(ctx iris.Context) {
ctx.StatusCode(404)
ctx.Writef("%s version not found", versioning.GetVersion(ctx))
}
userAPI := app.Party("/api/user")
userAPI.Get("/", myMiddleware, versioning.NewMatcher(versioning.Map{
"1.0": sendHandler(v10Response),
">= 2, < 3": sendHandler(v2Response),
versioning.NotFound: myCustomNotVersionFound,
}))
```
### Deprecation
Using the `versioning.Deprecated(handler iris.Handler, options versioning.DeprecationOptions) iris.Handler` function you can mark a specific handler version as deprecated.
```go
v10Handler := versioning.Deprecated(sendHandler(v10Response), versioning.DeprecationOptions{
// if empty defaults to: "WARNING! You are using a deprecated version of this API."
WarnMessage string
DeprecationDate time.Time
DeprecationInfo string
})
userAPI.Get("/", versioning.NewMatcher(versioning.Map{
"1.0": v10Handler,
// [...]
}))
```
This will make the handler to send these headers to the client:
- `"X-API-Warn": options.WarnMessage`
- `"X-API-Deprecation-Date": context.FormatTime(ctx, options.DeprecationDate))`
- `"X-API-Deprecation-Info": options.DeprecationInfo`
> versioning.DefaultDeprecationOptions can be passed instead if you don't care about Date and Info.
## Grouping routes by version
Grouping routes by version is possible as well.
Using the `versioning.NewGroup(version string) *versioning.Group` function you can create a group to register your versioned routes.
The `versioning.RegisterGroups(r iris.Party, versionNotFoundHandler iris.Handler, groups ...*versioning.Group)` must be called in the end in order to register the routes to a specific `Party`.
```go
app := iris.New()
userAPI := app.Party("/api/user")
// [... static serving, middlewares and etc goes here].
userAPIV10 := versioning.NewGroup("1.0")
userAPIV10.Get("/", sendHandler(v10Response))
userAPIV2 := versioning.NewGroup(">= 2, < 3")
userAPIV2.Get("/", sendHandler(v2Response))
userAPIV2.Post("/", sendHandler(v2Response))
userAPIV2.Put("/other", sendHandler(v2Response))
versioning.RegisterGroups(userAPI, versioning.NotFoundHandler, userAPIV10, userAPIV2)
```
> A middleware can be registered to the actual `iris.Party` only, using the methods we learnt above, i.e by using the `versioning.Match` in order to detect what code/handler you want to be executed when "x" or no version is requested.
### Deprecation for Group
Just call the `Deprecated(versioning.DeprecationOptions)` on the group you want to notify your API consumers that this specific version is deprecated.
```go
userAPIV10 := versioning.NewGroup("1.0").Deprecated(versioning.DefaultDeprecationOptions)
```
## Compare version manually from inside your handlers
```go
// reports if the "version" is matching to the "is".
// the "is" can be a constraint like ">= 1, < 3".
If(version string, is string) bool
```
```go
// same as `If` but expects a Context to read the requested version.
Match(ctx iris.Context, expectedVersion string) bool
```
```go
app.Get("/api/user", func(ctx iris.Context) {
if versioning.Match(ctx, ">= 2.2.3") {
// [logic for >= 2.2.3 version of your handler goes here]
return
}
})
```