diff --git a/_examples/README.md b/_examples/README.md index 1a1df09a..5b50e7c9 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -1,370 +1,195 @@ -# Examples - -Please do learn how [net/http](https://golang.org/pkg/net/http/) std package works, first. - -This folder provides easy to understand code snippets on how to get started with [iris](https://github.com/kataras/iris) web framework. - -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! - -## Running the examples - - - -1. Install the Go Programming Language, version 1.12+ from https://golang.org/dl. -2. [Install Iris](https://github.com/kataras/iris/wiki/installation) -3. Install any external packages that required by the examples - -
-External packages - -```sh -cd _examples && go get ./... -``` - -
- -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) -- Custom HTTP Server - * [HTTP/3 Quic](http-listening/http3-quic) - * [easy way](http-listening/custom-httpserver/easy-way/main.go) - * [std way](http-listening/custom-httpserver/std-way/main.go) - * [multi server instances](http-listening/custom-httpserver/multi/main.go) -- Graceful Shutdown - * [using the `RegisterOnInterrupt`](http-listening/graceful-shutdown/default-notifier/main.go) - * [using a custom notifier](http-listening/graceful-shutdown/custom-notifier/main.go) - -### Configuration - -- [Functional](configuration/functional/main.go) -- [From Configuration Struct](configuration/from-configuration-structure/main.go) -- [Import from YAML file](configuration/from-yaml-file/main.go) - * [Share Configuration between multiple instances](configuration/from-yaml-file/shared-configuration/main.go) -- [Import from TOML file](configuration/from-toml-file/main.go) - -### Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context - -* `app.Get("{userid:int min(1)}", myHandler)` -* `app.Post("{asset:path}", myHandler)` -* `app.Put("{custom:string regexp([a-z]+)}", myHandler)` - -Note: unlike other routers you'd seen, iris' router can handle things like these: -```go -// Matches all GET requests prefixed with "/assets/" -app.Get("/assets/{asset:path}", assetsWildcardHandler) - -// Matches only GET "/" -app.Get("/", indexHandler) -// Matches only GET "/about" -app.Get("/about", aboutHandler) - -// Matches all GET requests prefixed with "/profile/" -// and followed by a single path part -app.Get("/profile/{username:string}", userHandler) -// Matches only GET "/profile/me" because -// it does not conflict with /profile/{username:string} -// or the root wildcard {root:path} -app.Get("/profile/me", userHandler) - -// Matches all GET requests prefixed with /users/ -// and followed by a number which should be equal or bigger than 1 -app.Get("/user/{userid:int min(1)}", getUserHandler) -// Matches all requests DELETE prefixed with /users/ -// and following by a number which should be equal or bigger than 1 -app.Delete("/user/{userid:int min(1)}", deleteUserHandler) - -// Matches all GET requests except "/", "/about", anything starts with "/assets/" etc... -// because it does not conflict with the rest of the routes. -app.Get("{root:path}", rootWildcardHandler) -``` - -Navigate through examples for a better understanding. - -- [Overview](routing/overview/main.go) -- [Basic](routing/basic/main.go) -- [Controllers](mvc) -- [Custom HTTP Errors](routing/http-errors/main.go) -- [Not Found - Suggest Closest Paths](routing/not-found-suggests/main.go) **NEW** -- [Dynamic Path](routing/dynamic-path/main.go) - * [root level wildcard path](routing/dynamic-path/root-wildcard/main.go) -- [Write your own custom parameter types](routing/macros/main.go) -- [Reverse routing](routing/reverse/main.go) -- [Custom Router (high-level)](routing/custom-high-level-router/main.go) -- [Custom Wrapper](routing/custom-wrapper/main.go) -- Custom Context - * [method overriding](routing/custom-context/method-overriding/main.go) - * [new implementation](routing/custom-context/new-implementation/main.go) -- [Route State](routing/route-state/main.go) -- [Writing a middleware](routing/writing-a-middleware) - * [per-route](routing/writing-a-middleware/per-route/main.go) - * [globally](routing/writing-a-middleware/globally/main.go) -- [Route Register Rule](routing/route-register-rule/main.go) **NEW** - -### Versioning - -- [How it works](https://github.com/kataras/iris/blob/master/versioning/README.md) -- [Example](versioning/main.go) - -### Dependency Injection - -- [Basic](hero/basic/main.go) -- [Overview](hero/overview) -- [Sessions](hero/sessions) -- [Yet another dependency injection example and good practises at general](hero/smart-contract/main.go) - -### MVC - -- [Hello world](mvc/hello-world/main.go) -- [Basic](mvc/basic/main.go) -- [Basic: wildcard](mvc/basic/wildcard/main.go) **NEW** -- [Regexp](mvc/regexp/main.go) -- [Session Controller](mvc/session-controller/main.go) -- [Overview - Plus Repository and Service layers](mvc/overview) -- [Login showcase - Plus Repository and Service layers](mvc/login) -- [Singleton](mvc/singleton) -- [Websocket Controller](mvc/websocket) -- [Register Middleware](mvc/middleware) -- [Vue.js Todo MVC](tutorial/vuejs-todo-mvc) -- [gRPC-compatible controller](mvc/grpc-compatible/main.go) **NEW** - -### Subdomains - -- [Single](subdomains/single/main.go) -- [Multi](subdomains/multi/main.go) -- [Wildcard](subdomains/wildcard/main.go) -- [WWW](subdomains/www/main.go) -- [Redirect fast](subdomains/redirect/main.go) - -### Convert `http.Handler/HandlerFunc` - -- [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) -- [From func(http.HandlerFunc) http.HandlerFunc](convert-handlers/real-usecase-raven/writing-middleware/main.go) - -### View - -- [Overview](view/overview/main.go) -- [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) - * [BoltDB](sessions/database/boltdb/main.go) - * [Redis](sessions/database/redis/main.go) - -> You're free to use your own favourite sessions package if you'd like so. - -### Websockets - -- [Basic](websocket/basic) - * [Server](websocket/basic/server.go) - * [Go Client](websocket/basic/go-client/client.go) - * [Browser Client](websocket/basic/browser/index.html) - * [Browser NPM Client (browserify)](websocket/basic/browserify/app.js) -- [Native Messages](websocket/native-messages/main.go) -- [TLS Enabled](websocket/secure/README.md) - -### Hey, You - -Developers should read the [godocs](https://godoc.org/github.com/kataras/iris) and https://docs.iris-go.com for a better understanding. - -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! +# Table of Contents + +* Tutorials + * [Caddy](tutorial/caddy) + * [MongoDB](tutorial/mongodb) + * [Dropzone.js](tutorial/dropzonejs) + * [URL Shortener](tutorial/url-shortener/main.go) + * [Online Visitors](tutorial/online-visitors/main.go) + * [REST API for Apache Kafka](tutorial/api-for-apache-kafka) + * [Vue.js Todo (MVC)](tutorial/vuejs-todo-mvc) + * [gRPC (MVC)](mvc/grpc-compatible) +* HTTP Listening + * [HOST:PORT](http-listening/listen-addr/main.go) + * [Public Test Domain](http-listening/listen-addr-public/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) + * [Graceful Shutdown](http-listening/graceful-shutdown/default-notifier/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) + * 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) +* Configuration + * [Functional](configuration/functional/main.go) + * [Configuration Struct](configuration/from-configuration-structure/main.go) + * [Import from YAML](configuration/from-yaml-file/main.go) + * [Share Configuration across instances](configuration/from-yaml-file/shared-configuration/main.go) + * [Import from TOML](configuration/from-toml-file/main.go) +* Routing + * [Overview](routing/overview/main.go) + * [Basic](routing/basic/main.go) + * [Custom HTTP Errors](routing/http-errors/main.go) + * [Not Found - Suggest Closest Paths](routing/not-found-suggests/main.go) + * [Dynamic Path](routing/dynamic-path/main.go) + * [Root Wildcard](routing/dynamic-path/root-wildcard/main.go) + * [Implement a Parameter Type](routing/macros/main.go) + * Middleware + * [Per Route](routing/writing-a-middleware/per-route/main.go) + * [Globally](routing/writing-a-middleware/globally/main.go) + * [Route Register Rule](routing/route-register-rule/main.go) + * Convert net/http Handlers + * [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) + * [From func(http.HandlerFunc) http.HandlerFunc](convert-handlers/real-usecase-raven/writing-middleware/main.go) + * [Route State](routing/route-state/main.go) + * [Reverse Routing](routing/reverse/main.go) + * [Router Wrapper](routing/custom-wrapper/main.go) + * [Custom Router](routing/custom-high-level-router/main.go) + * Custom Context + * [Method Overriding](routing/custom-context/method-overriding/main.go) + * [New Implementation](routing/custom-context/new-implementation/main.go) +* Subdomains + * [Single](subdomains/single/main.go) + * [Multi](subdomains/multi/main.go) + * [Wildcard](subdomains/wildcard/main.go) + * [WWW](subdomains/www/main.go) + * [Redirection](subdomains/redirect/main.go) +* API Versioning + * [How it works](https://github.com/kataras/iris/wiki/API-versioning) + * [Example](versioning/main.go) +* API Documentation + * [yaag](apidoc/yaag/main.go) +* Testing + * [Example](testing/httptest/main_test.go) +* 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 Files (rate limiter included)](file-server/send-files/main.go) + * 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) + * [Embedded Single Page Application with other routes](file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go) +* View + * [Overview](view/overview/main.go) + * [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) + * Third-Parties + * [Render `valyala/quicktemplate` templates](http_responsewriter/quicktemplate) + * [Render `shiyanhui/hero` templates](http_responsewriter/herotemplate) +* Request Body + * [Bind JSON](http_request/read-json/main.go) + * * [Struct Validation](http_request/read-json-struct-validation/main.go) + * [Bind XML](http_request/read-xml/main.go) + * [Bind MsgPack](http_request/read-msgpack/main.go) + * [Bind YAML](http_request/read-yaml/main.go) + * [Bind Form](http_request/read-form/main.go) + * [Bind Query](http_request/read-query/main.go) + * [Bind Body](http_request/read-body/main.go) + * [Bind Custom per type](http_request/read-custom-per-type/main.go) + * [Bind Custom via Unmarshaler](http_request/read-custom-via-unmarshaler/main.go) + * [Bind Many times](http_request/read-many/main.go) + * [Upload/Read File](http_request/upload-file/main.go) + * [Upload multiple Files](http_request/upload-files/main.go) + * [Extract Referrer](http_request/extract-referer/main.go) +* Response Writer + * [Content Negotiation](http_responsewriter/content-negotiation) + * [Text, Markdown, YAML, HTML, JSON, JSONP, Msgpack, XML and 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) +* Cache + * [Simple](cache/simple/main.go) + * [Client-Side (304)](cache/client-side/main.go) +* Localization and Internationalization + * [i18n](i18n/main.go) +* Sitemaps + * [Sitemap](sitemap/main.go) +* Authentication + * [Basic Authentication](authentication/basicauth/main.go) + * [OAUth2](authentication/oauth2/main.go) + * [Request Auth(JWT)](experimental-handlers/jwt/main.go) + * [Manage Permissions](permissions/main.go) +* Cookies + * [Basic](cookies/basic/main.go) + * [Encode/Decode (with `securecookie`)](cookies/securecookie/main.go) +* 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) + * [BoltDB](sessions/database/boltdb/main.go) + * [Redis](sessions/database/redis/main.go) +* Websocket + * [Basic](websocket/basic) + * [Server](websocket/basic/server.go) + * [Go Client](websocket/basic/go-client/client.go) + * [Browser Client](websocket/basic/browser/index.html) + * [Browser NPM Client (browserify)](websocket/basic/browserify/app.js) + * [Native Messages](websocket/native-messages/main.go) + * [TLS](websocket/secure/README.md) +* Dependency Injection + * [Overview (Movies Service)](ependency-injection/overview/main.go) + * [Basic](dependency-injection/basic/main.go) + * [Middleware](dependency-injection/basic/middleware/main.go) + * [Sessions](dependency-injection/sessions/main.go) + * [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) diff --git a/_examples/configuration/README.md b/_examples/configuration/README.md deleted file mode 100644 index a018c6f6..00000000 --- a/_examples/configuration/README.md +++ /dev/null @@ -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("Hello!") - }) - // [...] - - // 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("Hello!") - }) - // [...] - - // 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("Hello!") - }) - // [...] - - // 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("Hello!") - }) - // [...] - - 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") -} -``` \ No newline at end of file diff --git a/_examples/hero/overview/datamodels/movie.go b/_examples/dependency-injection/overview/datamodels/movie.go similarity index 95% rename from _examples/hero/overview/datamodels/movie.go rename to _examples/dependency-injection/overview/datamodels/movie.go index 7649d487..9797b538 100644 --- a/_examples/hero/overview/datamodels/movie.go +++ b/_examples/dependency-injection/overview/datamodels/movie.go @@ -10,7 +10,7 @@ package datamodels // as the only one Movie model in our application, // for the shake of simplicty. type Movie struct { - ID int64 `json:"id"` + ID uint64 `json:"id"` Name string `json:"name"` Year int `json:"year"` Genre string `json:"genre"` diff --git a/_examples/hero/overview/datasource/movies.go b/_examples/dependency-injection/overview/datasource/movies.go similarity index 86% rename from _examples/hero/overview/datasource/movies.go rename to _examples/dependency-injection/overview/datasource/movies.go index ee07c357..4b5fbf31 100644 --- a/_examples/hero/overview/datasource/movies.go +++ b/_examples/dependency-injection/overview/datasource/movies.go @@ -2,10 +2,10 @@ 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. -var Movies = map[int64]datamodels.Movie{ +var Movies = map[uint64]datamodels.Movie{ 1: { ID: 1, Name: "Casablanca", diff --git a/_examples/dependency-injection/overview/main.go b/_examples/dependency-injection/overview/main.go new file mode 100644 index 00000000..8386aaae --- /dev/null +++ b/_examples/dependency-injection/overview/main.go @@ -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, + ) +} diff --git a/_examples/hero/overview/repositories/movie_repository.go b/_examples/dependency-injection/overview/repositories/movie_repository.go similarity index 95% rename from _examples/hero/overview/repositories/movie_repository.go rename to _examples/dependency-injection/overview/repositories/movie_repository.go index 491d1785..0a46cb1b 100644 --- a/_examples/hero/overview/repositories/movie_repository.go +++ b/_examples/dependency-injection/overview/repositories/movie_repository.go @@ -6,7 +6,7 @@ import ( "errors" "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. @@ -27,14 +27,14 @@ type MovieRepository interface { // NewMovieRepository returns a new movie memory-based repository, // 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} } // movieMemoryRepository is a "MovieRepository" // which manages the movies using the memory data source (map). type movieMemoryRepository struct { - source map[int64]datamodels.Movie + source map[uint64]datamodels.Movie mu sync.RWMutex } @@ -115,7 +115,7 @@ func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamode id := movie.ID if id == 0 { // Create new action - var lastID int64 + var lastID uint64 // find the biggest ID in order to not have duplications // in productions apps you can use a third-party // library to generate a UUID as string. diff --git a/_examples/hero/overview/services/movie_service.go b/_examples/dependency-injection/overview/services/movie_service.go similarity index 72% rename from _examples/hero/overview/services/movie_service.go rename to _examples/dependency-injection/overview/services/movie_service.go index 109e88d9..6835a9b5 100644 --- a/_examples/hero/overview/services/movie_service.go +++ b/_examples/dependency-injection/overview/services/movie_service.go @@ -3,8 +3,8 @@ package services import ( - "github.com/kataras/iris/v12/_examples/hero/overview/datamodels" - "github.com/kataras/iris/v12/_examples/hero/overview/repositories" + "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels" + "github.com/kataras/iris/v12/_examples/dependency-injection/overview/repositories" ) // 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. type MovieService interface { GetAll() []datamodels.Movie - GetByID(id int64) (datamodels.Movie, bool) - DeleteByID(id int64) bool - UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error) + GetByID(id uint64) (datamodels.Movie, bool) + DeleteByID(id uint64) bool + UpdatePosterAndGenreByID(id uint64, poster string, genre string) (datamodels.Movie, error) } // NewMovieService returns the default movie service. @@ -39,14 +39,14 @@ func (s *movieService) GetAll() []datamodels.Movie { } // 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 m.ID == id }) } // 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. return s.repo.InsertOrUpdate(datamodels.Movie{ ID: id, @@ -58,7 +58,7 @@ func (s *movieService) UpdatePosterAndGenreByID(id int64, poster string, genre s // DeleteByID deletes a movie by its id. // // 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 m.ID == id }, 1) diff --git a/_examples/hero/overview/web/middleware/basicauth.go b/_examples/dependency-injection/overview/web/middleware/basicauth.go similarity index 100% rename from _examples/hero/overview/web/middleware/basicauth.go rename to _examples/dependency-injection/overview/web/middleware/basicauth.go diff --git a/_examples/hero/overview/web/routes/hello.go b/_examples/dependency-injection/overview/web/routes/hello.go similarity index 100% rename from _examples/hero/overview/web/routes/hello.go rename to _examples/dependency-injection/overview/web/routes/hello.go diff --git a/_examples/hero/overview/web/routes/movies.go b/_examples/dependency-injection/overview/web/routes/movies.go similarity index 79% rename from _examples/hero/overview/web/routes/movies.go rename to _examples/dependency-injection/overview/web/routes/movies.go index 9b6af164..d6b902d9 100644 --- a/_examples/hero/overview/web/routes/movies.go +++ b/_examples/dependency-injection/overview/web/routes/movies.go @@ -5,8 +5,8 @@ package routes import ( "errors" - "github.com/kataras/iris/v12/_examples/hero/overview/datamodels" - "github.com/kataras/iris/v12/_examples/hero/overview/services" + "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels" + "github.com/kataras/iris/v12/_examples/dependency-injection/overview/services" "github.com/kataras/iris/v12" ) @@ -21,14 +21,14 @@ func Movies(service services.MovieService) (results []datamodels.Movie) { // MovieByID returns a movie. // Demo: // 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. } // UpdateMovieByID updates a movie. // Demo: // 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 file, info, err := ctx.FormFile("poster") if err != nil { @@ -47,7 +47,7 @@ func UpdateMovieByID(ctx iris.Context, service services.MovieService, id int64) // DeleteMovieByID deletes a movie. // Demo: // 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) if wasDel { // return the deleted movie's ID diff --git a/_examples/hero/overview/web/views/hello/index.html b/_examples/dependency-injection/overview/web/views/hello/index.html similarity index 100% rename from _examples/hero/overview/web/views/hello/index.html rename to _examples/dependency-injection/overview/web/views/hello/index.html diff --git a/_examples/hero/overview/web/views/hello/name.html b/_examples/dependency-injection/overview/web/views/hello/name.html similarity index 100% rename from _examples/hero/overview/web/views/hello/name.html rename to _examples/dependency-injection/overview/web/views/hello/name.html diff --git a/_examples/dependency-injection/sessions/main.go b/_examples/dependency-injection/sessions/main.go new file mode 100644 index 00000000..2172c34c --- /dev/null +++ b/_examples/dependency-injection/sessions/main.go @@ -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) +} diff --git a/_examples/dependency-injection/sessions/routes/index.go b/_examples/dependency-injection/sessions/routes/index.go new file mode 100644 index 00000000..d1660563 --- /dev/null +++ b/_examples/dependency-injection/sessions/routes/index.go @@ -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) +} diff --git a/_examples/hero/smart-contract/main.go b/_examples/dependency-injection/smart-contract/main.go similarity index 81% rename from _examples/hero/smart-contract/main.go rename to _examples/dependency-injection/smart-contract/main.go index fd635309..f3c93f33 100644 --- a/_examples/hero/smart-contract/main.go +++ b/_examples/dependency-injection/smart-contract/main.go @@ -5,8 +5,6 @@ import ( "strings" "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, // see `sendJSON` for more. @@ -17,19 +15,9 @@ import ( $ 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() { app := newApp() + app.Logger().SetLevel("debug") // http://localhost:8080/users?query=[?Name == 'John Doe'].Age // <- client will receive the age of a user which his name is "John Doe". @@ -45,6 +33,17 @@ func main() { 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 */ @@ -52,7 +51,7 @@ func main() { func registerUsersRoutes(usersRouter iris.Party) { // GET: /users usersRouter.Get("/", getAllUsersHandler) - usersRouter.PartyFunc("/{name:string}", registerUserRoutes) + usersRouter.Party("/{name}").ConfigureContainer(registerUserRoutes) } type user struct { @@ -78,16 +77,12 @@ func getAllUsersHandler(ctx iris.Context) { START OF USERS.USER SUB ROUTER */ -func registerUserRoutes(userRouter iris.Party) { - // create a new dependency injection manager for this sub router. - 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) - +func registerUserRoutes(userRouter *iris.APIContainer) { + userRouter.RegisterDependency(userDependency) // GET: /users/{name:string} - userRouter.Get("/", userDeps.Handler(getUserHandler)) + userRouter.Get("/", getUserHandler) // GET: /users/{name:string}/age - userRouter.Get("/age", userDeps.Handler(getUserAgeHandler)) + userRouter.Get("/age", getUserAgeHandler) } 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) { - if u == nil { - return - } - 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 { - if u == nil { - return "" - } - return fmt.Sprintf("%d", u.Age) } -*/ /* 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.StatusCode(statusCode) - ctx.JSON(err) - // 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. @@ -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 } diff --git a/_examples/hero/basic/README.md b/_examples/hero/basic/README.md deleted file mode 100644 index 88309fb5..00000000 --- a/_examples/hero/basic/README.md +++ /dev/null @@ -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) \ No newline at end of file diff --git a/_examples/hero/basic/main.go b/_examples/hero/basic/main.go deleted file mode 100644 index 88214b14..00000000 --- a/_examples/hero/basic/main.go +++ /dev/null @@ -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 -} diff --git a/_examples/hero/overview/main.go b/_examples/hero/overview/main.go deleted file mode 100644 index 264ed5b1..00000000 --- a/_examples/hero/overview/main.go +++ /dev/null @@ -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, - ) -} diff --git a/_examples/hero/sessions/main.go b/_examples/hero/sessions/main.go deleted file mode 100644 index 4a9da3c1..00000000 --- a/_examples/hero/sessions/main.go +++ /dev/null @@ -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") -} diff --git a/_examples/hero/sessions/routes/index.go b/_examples/hero/sessions/routes/index.go deleted file mode 100644 index 2d3611ad..00000000 --- a/_examples/hero/sessions/routes/index.go +++ /dev/null @@ -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. -*/ diff --git a/_examples/subdomains/single/main.go b/_examples/subdomains/single/main.go index 2ecb7f43..ef972c05 100644 --- a/_examples/subdomains/single/main.go +++ b/_examples/subdomains/single/main.go @@ -8,10 +8,8 @@ import ( func main() { app := iris.New() - // subdomains works with all available routers, like other features too. - - // no order, you can register subdomains at the end also. - admin := app.Party("admin.") + // Subdomain method is just another Party. + admin := app.Subdomain("admin") { // admin.mydomain.com admin.Get("/", func(c iris.Context) { @@ -27,7 +25,7 @@ func main() { }) } - // mydomain.com/ + // mydomain.com app.Get("/", func(c iris.Context) { c.Writef("INDEX FROM no-subdomain hey") }) diff --git a/_examples/tutorial/vuejs-todo-mvc/README.md b/_examples/tutorial/vuejs-todo-mvc/README.md index 63a67671..34296b4d 100644 --- a/_examples/tutorial/vuejs-todo-mvc/README.md +++ b/_examples/tutorial/vuejs-todo-mvc/README.md @@ -498,57 +498,65 @@ And finally our main application's endpoint. package main import ( - "vuejs-todo-mvc/todo" - "vuejs-todo-mvc/web/controllers" + "strings" - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/sessions" - "github.com/kataras/iris/v12/websocket" + "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/mvc" + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/mvc" + "github.com/kataras/iris/v12/sessions" + "github.com/kataras/iris/v12/websocket" ) func main() { - app := iris.New() + app := iris.New() - // serve our app in public, public folder - // contains the client-side vue.js application, - // no need for any server-side template here, - // actually if you're going to just use vue without any - // back-end services, you can just stop afer this line and start the server. - app.HandleDir("/", "./public") + // serve our app in public, public folder + // contains the client-side vue.js application, + // no need for any server-side template here, + // actually if you're going to just use vue without any + // back-end services, you can just stop afer this line and start the server. + app.HandleDir("/", "./public") - // configure the http sessions. - sess := sessions.New(sessions.Config{ - Cookie: "iris_session", - }) + // configure the http sessions. + sess := sessions.New(sessions.Config{ + Cookie: "iris_session", + }) - // configure the websocket server. - ws := websocket.New(websocket.Config{}) + // create a sub router and register the http controllers. + todosRouter := app.Party("/todos") - // 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") - // 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. + todosApp := mvc.New(todosRouter) - // create our mvc application targeted to /todos relative sub path. - todosApp := mvc.New(todosRouter) + // any dependencies bindings here... + todosApp.Register( + todo.NewMemoryService(), + ) - // any dependencies bindings here... - todosApp.Register( - todo.NewMemoryService(), - sess.Start, - ws.Upgrade, - ) + todosController := new(controllers.TodoController) + // controllers registration here... + todosApp.Handle(todosController) - // controllers registration here... - todosApp.Handle(new(controllers.TodoController)) + // 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 + }) - // start the web server at http://localhost:8080 - app.Listen(":8080") + 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 + app.Listen(":8080") } ``` diff --git a/_examples/tutorial/vuejs-todo-mvc/src/web/main.go b/_examples/tutorial/vuejs-todo-mvc/src/web/main.go index 75f0800d..a47989e0 100644 --- a/_examples/tutorial/vuejs-todo-mvc/src/web/main.go +++ b/_examples/tutorial/vuejs-todo-mvc/src/web/main.go @@ -36,7 +36,6 @@ func main() { // any dependencies bindings here... todosApp.Register( todo.NewMemoryService(), - sess.Start, ) todosController := new(controllers.TodoController) diff --git a/core/router/api_container.go b/core/router/api_container.go index 5a565f70..ab1d4f03 100644 --- a/core/router/api_container.go +++ b/core/router/api_container.go @@ -26,6 +26,14 @@ func (api *APIContainer) Party(relativePath string, handlersFn ...interface{}) * 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). // The "errorHandler" handles any error may occurred and returned // 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 } +// 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. // // See `OnError`, `RegisterDependency`, `Done` and `Handle` for more. diff --git a/core/router/route.go b/core/router/route.go index 31b51d91..b89650ad 100644 --- a/core/router/route.go +++ b/core/router/route.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "net/http" + "path/filepath" "strings" "time" @@ -342,7 +343,7 @@ func (r *Route) ResolvePath(args ...string) 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) { return "" diff --git a/hero/func_result.go b/hero/func_result.go index bd6d1ab7..70670e4a 100644 --- a/hero/func_result.go +++ b/hero/func_result.go @@ -54,7 +54,7 @@ func defaultResultHandler(ctx context.Context, v interface{}) error { // All types that complete this interface // 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 { // Dispatch should send a response to the client. 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. // 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 { Name string Layout string diff --git a/hero/handler.go b/hero/handler.go index 4e6b72e6..ca9512e9 100644 --- a/hero/handler.go +++ b/hero/handler.go @@ -103,6 +103,13 @@ func makeHandler(fn interface{}, c *Container, paramsCount int) context.Handler 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 } diff --git a/versioning/README.md b/versioning/README.md deleted file mode 100644 index 01a629ad..00000000 --- a/versioning/README.md +++ /dev/null @@ -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 - } -}) -``` \ No newline at end of file