Update to version 8.5.5

Former-commit-id: b5be58709f17758a8df3ebc99270b97ccd8b18f2
This commit is contained in:
kataras 2017-11-02 05:50:56 +02:00
parent 6607008054
commit 666bcacf20
97 changed files with 38154 additions and 38162 deletions

View File

@ -1,374 +1,374 @@
# 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) micro 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!
### Overview
- [Hello world!](hello-world/main.go)
- [Glimpse](overview/main.go)
- [Tutorial: Online Visitors](tutorial/online-visitors/main.go)
- [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)
### 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)
* [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
* [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)
- [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)
- [Dynamic Path](routing/dynamic-path/main.go)
* [root level wildcard path](routing/dynamic-path/root-wildcard/main.go)
- [Reverse routing](routing/reverse/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)
### MVC
![](mvc/web_mvc_diagram.png)
Iris has **first-class support for the MVC (Model View Controller) pattern**, you'll not find
these stuff anywhere else in the Go world.
Iris web framework supports Request data, Models, Persistence Data and Binding
with the fastest possible execution.
**Characteristics**
All HTTP Methods are supported, for example if want to serve `GET`
then the controller should have a function named `Get()`,
you can define more than one method function to serve in the same Controller struct.
Persistence data inside your Controller struct (share data between requests)
via `iris:"persistence"` tag right to the field or Bind using `app.Controller("/" , new(myController), theBindValue)`.
Models inside your Controller struct (set-ed at the Method function and rendered by the View)
via `iris:"model"` tag right to the field, i.e ```User UserModel `iris:"model" name:"user"` ``` view will recognise it as `{{.user}}`.
If `name` tag is missing then it takes the field's name, in this case the `"User"`.
Access to the request path and its parameters via the `Path and Params` fields.
Access to the template file that should be rendered via the `Tmpl` field.
Access to the template data that should be rendered inside
the template file via `Data` field.
Access to the template layout via the `Layout` field.
Access to the low-level `iris.Context/context.Context` via the `Ctx` field.
Flow as you used to, `Controllers` can be registered to any `Party`,
including Subdomains, the Party's begin and done handlers work as expected.
Optional `BeginRequest(ctx)` function to perform any initialization before the method execution,
useful to call middlewares or when many methods use the same collection of data.
Optional `EndRequest(ctx)` function to perform any finalization after any method executed.
Inheritance, see for example our `mvc.SessionController`, it has the `mvc.Controller` as an embedded field
and it adds its logic to its `BeginRequest`, [here](https://github.com/kataras/iris/blob/master/mvc/session_controller.go).
Register one or more relative paths and able to get path parameters, i.e
If `app.Controller("/user", new(user.Controller))`
- `func(*Controller) Get()` - `GET:/user` , as usual.
- `func(*Controller) Post()` - `POST:/user`, as usual.
- `func(*Controller) GetLogin()` - `GET:/user/login`
- `func(*Controller) PostLogin()` - `POST:/user/login`
- `func(*Controller) GetProfileFollowers()` - `GET:/user/profile/followers`
- `func(*Controller) PostProfileFollowers()` - `POST:/user/profile/followers`
- `func(*Controller) GetBy(id int64)` - `GET:/user/{param:long}`
- `func(*Controller) PostBy(id int64)` - `POST:/user/{param:long}`
If `app.Controller("/profile", new(profile.Controller))`
- `func(*Controller) GetBy(username string)` - `GET:/profile/{param:string}`
If `app.Controller("/assets", new(file.Controller))`
- `func(*Controller) GetByWildard(path string)` - `GET:/assets/{param:path}`
Supported types for method functions receivers: int, int64, bool and string.
Response via output arguments, optionally, i.e
```go
func(c *ExampleController) Get() string |
(string, string) |
(string, int) |
int |
(int, string) |
(string, error) |
bool |
(any, bool) |
(bool, any) |
error |
(int, error) |
(customStruct, error) |
customStruct |
(customStruct, int) |
(customStruct, string) |
mvc.Result or (mvc.Result, error) and so on...
```
where [mvc.Result](https://github.com/kataras/iris/blob/master/mvc/method_result.go) is an interface which contains only that function: `Dispatch(ctx iris.Context)`.
**Using Iris MVC for code reuse**
By creating components that are independent of one another, developers are able to reuse components quickly and easily in other applications. The same (or similar) view for one application can be refactored for another application with different data because the view is simply handling how the data is being displayed to the user.
If you're new to back-end web development read about the MVC architectural pattern first, a good start is that [wikipedia article](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller).
Follow the examples below,
- [Hello world](mvc/hello-world/main.go) **UPDATED**
- [Session Controller](mvc/session-controller/main.go) **UPDATED**
- [Overview - Plus Repository and Service layers](mvc/overview) **NEW**
- [Login showcase - Plus Repository and Service layers](mvc/login) **NEW**
<!--
Why updated?
Old method works, as promised no breaking changes.
But mvc.C as controller marker and mvc.Result on method functions return value
is more lightweight and faster than `mvc.Controller` because `mvc.Controller` initializes
some fields like `Data, Path`... and Data is a map even if not used, at the opossite hand
`mvc.C` just initializes the context `Ctx` field, the dev has all the `mvc.Controller`'s features
by the `mvc.Result` built'n types like `mvc.Response` and `mvc.View` PLUS she/he can
convert any custom type into a response dispatcher by implementing the `mvc.Result` interface.
-->
### Subdomains
- [Single](subdomains/single/main.go)
- [Multi](subdomains/multi/main.go)
- [Wildcard](subdomains/wildcard/main.go)
- [WWW](subdomains/www/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
| Engine | Declaration |
| -----------|-------------|
| template/html | `iris.HTML(...)` |
| django | `iris.Django(...)` |
| handlebars | `iris.Handlebars(...)` |
| amber | `iris.Amber(...)` |
| pug(jade) | `iris.Pug(...)` |
- [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)
You can serve [quicktemplate](https://github.com/valyala/quicktemplate) files too, simply by using the `context#ResponseWriter`, take a look at the [http_responsewriter/quicktemplate](http_responsewriter/quicktemplate) example.
### Authentication
- [Basic Authentication](authentication/basicauth/main.go)
- [OAUth2](authentication/oauth2/main.go)
- [JWT](https://github.com/iris-contrib/middleware/blob/master/jwt/_example/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)
- [Send/Force-Download Files](file-server/send-files/main.go)
- 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)
### How to Read from `context.Request() *http.Request`
- [Bind JSON](http_request/read-json/main.go)
- [Bind Form](http_request/read-form/main.go)
- [Upload/Read Files](http_request/upload-files/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`
- [Write `valyala/quicktemplate` templates](http_responsewriter/quicktemplate)
- [Text, Markdown, HTML, JSON, JSONP, XML, Binary](http_responsewriter/write-rest/main.go)
- [Stream Writer](http_responsewriter/stream-writer/main.go)
- [Transactions](http_responsewriter/transactions/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)
### Miscellaneous
- [Request Logger](http_request/request-logger/main.go)
* [log requests to a file](http_request/request-logger/request-logger-file/main.go)
- [Localization and Internationalization](miscellaneous/i18n/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)
#### 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)
> You're free to use your own favourite caching package if you'd like so.
### Sessions
iris session manager lives on its own [package](https://github.com/kataras/iris/tree/master/sessions).
- [Overview](sessions/overview/main.go)
- [Standalone](sessions/standalone/main.go)
- [Secure Cookie](sessions/securecookie/main.go)
- [Flash Messages](sessions/flash-messages/main.go)
- [Databases](sessions/database)
* [File](sessions/database/file/main.go)
* [BoltDB](sessions/database/boltdb/main.go)
* [Badger](sessions/database/badger/main.go)
* [LevelDB](sessions/database/leveldb/main.go)
* [Redis](sessions/database/redis/main.go)
> You're free to use your own favourite sessions package if you'd like so.
### Websockets
iris websocket library lives on its own [package](https://github.com/kataras/iris/tree/master/websocket).
The package is designed to work with raw websockets although its API is similar to the famous [socket.io](https://socket.io). I have read an article recently and I felt very contented about my decision to design a **fast** websocket-**only** package for Iris and not a backwards socket.io-like package. You can read that article by following this link: https://medium.com/@ivanderbyl/why-you-don-t-need-socket-io-6848f1c871cd.
- [Chat](websocket/chat/main.go)
- [Native Messages](websocket/native-messages/main.go)
- [Connection List](websocket/connectionlist/main.go)
- [TLS Enabled](websocket/secure/main.go)
- [Custom Raw Go Client](websocket/custom-go-client/main.go)
- [Third-Party socket.io](websocket/third-party-socketio/main.go)
> You're free to use your own favourite websockets package if you'd like so.
### Typescript Automation Tools
typescript automation tools have their own repository: [https://github.com/kataras/iris/tree/master/typescript](https://github.com/kataras/iris/tree/master/typescript) **it contains examples**
> I'd like to tell you that you can use your favourite but I don't think you will find such a thing anywhere else.
### 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!
# 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) micro 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!
### Overview
- [Hello world!](hello-world/main.go)
- [Glimpse](overview/main.go)
- [Tutorial: Online Visitors](tutorial/online-visitors/main.go)
- [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)
### 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)
* [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
* [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)
- [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)
- [Dynamic Path](routing/dynamic-path/main.go)
* [root level wildcard path](routing/dynamic-path/root-wildcard/main.go)
- [Reverse routing](routing/reverse/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)
### MVC
![](mvc/web_mvc_diagram.png)
Iris has **first-class support for the MVC (Model View Controller) pattern**, you'll not find
these stuff anywhere else in the Go world.
Iris web framework supports Request data, Models, Persistence Data and Binding
with the fastest possible execution.
**Characteristics**
All HTTP Methods are supported, for example if want to serve `GET`
then the controller should have a function named `Get()`,
you can define more than one method function to serve in the same Controller struct.
Persistence data inside your Controller struct (share data between requests)
via `iris:"persistence"` tag right to the field or Bind using `app.Controller("/" , new(myController), theBindValue)`.
Models inside your Controller struct (set-ed at the Method function and rendered by the View)
via `iris:"model"` tag right to the field, i.e ```User UserModel `iris:"model" name:"user"` ``` view will recognise it as `{{.user}}`.
If `name` tag is missing then it takes the field's name, in this case the `"User"`.
Access to the request path and its parameters via the `Path and Params` fields.
Access to the template file that should be rendered via the `Tmpl` field.
Access to the template data that should be rendered inside
the template file via `Data` field.
Access to the template layout via the `Layout` field.
Access to the low-level `iris.Context/context.Context` via the `Ctx` field.
Flow as you used to, `Controllers` can be registered to any `Party`,
including Subdomains, the Party's begin and done handlers work as expected.
Optional `BeginRequest(ctx)` function to perform any initialization before the method execution,
useful to call middlewares or when many methods use the same collection of data.
Optional `EndRequest(ctx)` function to perform any finalization after any method executed.
Inheritance, see for example our `mvc.SessionController`, it has the `mvc.Controller` as an embedded field
and it adds its logic to its `BeginRequest`, [here](https://github.com/kataras/iris/blob/master/mvc/session_controller.go).
Register one or more relative paths and able to get path parameters, i.e
If `app.Controller("/user", new(user.Controller))`
- `func(*Controller) Get()` - `GET:/user` , as usual.
- `func(*Controller) Post()` - `POST:/user`, as usual.
- `func(*Controller) GetLogin()` - `GET:/user/login`
- `func(*Controller) PostLogin()` - `POST:/user/login`
- `func(*Controller) GetProfileFollowers()` - `GET:/user/profile/followers`
- `func(*Controller) PostProfileFollowers()` - `POST:/user/profile/followers`
- `func(*Controller) GetBy(id int64)` - `GET:/user/{param:long}`
- `func(*Controller) PostBy(id int64)` - `POST:/user/{param:long}`
If `app.Controller("/profile", new(profile.Controller))`
- `func(*Controller) GetBy(username string)` - `GET:/profile/{param:string}`
If `app.Controller("/assets", new(file.Controller))`
- `func(*Controller) GetByWildard(path string)` - `GET:/assets/{param:path}`
Supported types for method functions receivers: int, int64, bool and string.
Response via output arguments, optionally, i.e
```go
func(c *ExampleController) Get() string |
(string, string) |
(string, int) |
int |
(int, string) |
(string, error) |
bool |
(any, bool) |
(bool, any) |
error |
(int, error) |
(customStruct, error) |
customStruct |
(customStruct, int) |
(customStruct, string) |
mvc.Result or (mvc.Result, error) and so on...
```
where [mvc.Result](https://github.com/kataras/iris/blob/master/mvc/method_result.go) is an interface which contains only that function: `Dispatch(ctx iris.Context)`.
**Using Iris MVC for code reuse**
By creating components that are independent of one another, developers are able to reuse components quickly and easily in other applications. The same (or similar) view for one application can be refactored for another application with different data because the view is simply handling how the data is being displayed to the user.
If you're new to back-end web development read about the MVC architectural pattern first, a good start is that [wikipedia article](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller).
Follow the examples below,
- [Hello world](mvc/hello-world/main.go) **UPDATED**
- [Session Controller](mvc/session-controller/main.go) **UPDATED**
- [Overview - Plus Repository and Service layers](mvc/overview) **NEW**
- [Login showcase - Plus Repository and Service layers](mvc/login) **NEW**
<!--
Why updated?
Old method works, as promised no breaking changes.
But mvc.C as controller marker and mvc.Result on method functions return value
is more lightweight and faster than `mvc.Controller` because `mvc.Controller` initializes
some fields like `Data, Path`... and Data is a map even if not used, at the opossite hand
`mvc.C` just initializes the context `Ctx` field, the dev has all the `mvc.Controller`'s features
by the `mvc.Result` built'n types like `mvc.Response` and `mvc.View` PLUS she/he can
convert any custom type into a response dispatcher by implementing the `mvc.Result` interface.
-->
### Subdomains
- [Single](subdomains/single/main.go)
- [Multi](subdomains/multi/main.go)
- [Wildcard](subdomains/wildcard/main.go)
- [WWW](subdomains/www/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
| Engine | Declaration |
| -----------|-------------|
| template/html | `iris.HTML(...)` |
| django | `iris.Django(...)` |
| handlebars | `iris.Handlebars(...)` |
| amber | `iris.Amber(...)` |
| pug(jade) | `iris.Pug(...)` |
- [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)
You can serve [quicktemplate](https://github.com/valyala/quicktemplate) files too, simply by using the `context#ResponseWriter`, take a look at the [http_responsewriter/quicktemplate](http_responsewriter/quicktemplate) example.
### Authentication
- [Basic Authentication](authentication/basicauth/main.go)
- [OAUth2](authentication/oauth2/main.go)
- [JWT](https://github.com/iris-contrib/middleware/blob/master/jwt/_example/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)
- [Send/Force-Download Files](file-server/send-files/main.go)
- 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)
### How to Read from `context.Request() *http.Request`
- [Bind JSON](http_request/read-json/main.go)
- [Bind Form](http_request/read-form/main.go)
- [Upload/Read Files](http_request/upload-files/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`
- [Write `valyala/quicktemplate` templates](http_responsewriter/quicktemplate)
- [Text, Markdown, HTML, JSON, JSONP, XML, Binary](http_responsewriter/write-rest/main.go)
- [Stream Writer](http_responsewriter/stream-writer/main.go)
- [Transactions](http_responsewriter/transactions/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)
### Miscellaneous
- [Request Logger](http_request/request-logger/main.go)
* [log requests to a file](http_request/request-logger/request-logger-file/main.go)
- [Localization and Internationalization](miscellaneous/i18n/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)
#### 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)
> You're free to use your own favourite caching package if you'd like so.
### Sessions
iris session manager lives on its own [package](https://github.com/kataras/iris/tree/master/sessions).
- [Overview](sessions/overview/main.go)
- [Standalone](sessions/standalone/main.go)
- [Secure Cookie](sessions/securecookie/main.go)
- [Flash Messages](sessions/flash-messages/main.go)
- [Databases](sessions/database)
* [File](sessions/database/file/main.go)
* [BoltDB](sessions/database/boltdb/main.go)
* [Badger](sessions/database/badger/main.go)
* [LevelDB](sessions/database/leveldb/main.go)
* [Redis](sessions/database/redis/main.go)
> You're free to use your own favourite sessions package if you'd like so.
### Websockets
iris websocket library lives on its own [package](https://github.com/kataras/iris/tree/master/websocket).
The package is designed to work with raw websockets although its API is similar to the famous [socket.io](https://socket.io). I have read an article recently and I felt very contented about my decision to design a **fast** websocket-**only** package for Iris and not a backwards socket.io-like package. You can read that article by following this link: https://medium.com/@ivanderbyl/why-you-don-t-need-socket-io-6848f1c871cd.
- [Chat](websocket/chat/main.go)
- [Native Messages](websocket/native-messages/main.go)
- [Connection List](websocket/connectionlist/main.go)
- [TLS Enabled](websocket/secure/main.go)
- [Custom Raw Go Client](websocket/custom-go-client/main.go)
- [Third-Party socket.io](websocket/third-party-socketio/main.go)
> You're free to use your own favourite websockets package if you'd like so.
### Typescript Automation Tools
typescript automation tools have their own repository: [https://github.com/kataras/iris/tree/master/typescript](https://github.com/kataras/iris/tree/master/typescript) **it contains examples**
> I'd like to tell you that you can use your favourite but I don't think you will find such a thing anywhere else.
### 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!

View File

@ -1,6 +1,6 @@
# Authentication
- [Basic Authentication](basicauth/main.go)
- [OAUth2](oauth2/main.go)
- [JWT](https://github.com/iris-contrib/middleware/blob/master/jwt/_example/main.go)
# Authentication
- [Basic Authentication](basicauth/main.go)
- [OAUth2](oauth2/main.go)
- [JWT](https://github.com/iris-contrib/middleware/blob/master/jwt/_example/main.go)
- [Sessions](https://github.com/kataras/iris/tree/master/_examples/#sessions)

View File

@ -1,3 +1,3 @@
{{range $key,$value:=.Providers}}
<p><a href="/auth/{{$value}}">Log in with {{index $.ProvidersMap $value}}</a></p>
{{range $key,$value:=.Providers}}
<p><a href="/auth/{{$value}}">Log in with {{index $.ProvidersMap $value}}</a></p>
{{end}}

View File

@ -1,11 +1,11 @@
<p><a href="/logout/{{.Provider}}">logout</a></p>
<p>Name: {{.Name}} [{{.LastName}}, {{.FirstName}}]</p>
<p>Email: {{.Email}}</p>
<p>NickName: {{.NickName}}</p>
<p>Location: {{.Location}}</p>
<p>AvatarURL: {{.AvatarURL}} <img src="{{.AvatarURL}}"></p>
<p>Description: {{.Description}}</p>
<p>UserID: {{.UserID}}</p>
<p>AccessToken: {{.AccessToken}}</p>
<p>ExpiresAt: {{.ExpiresAt}}</p>
<p><a href="/logout/{{.Provider}}">logout</a></p>
<p>Name: {{.Name}} [{{.LastName}}, {{.FirstName}}]</p>
<p>Email: {{.Email}}</p>
<p>NickName: {{.NickName}}</p>
<p>Location: {{.Location}}</p>
<p>AvatarURL: {{.AvatarURL}} <img src="{{.AvatarURL}}"></p>
<p>Description: {{.Description}}</p>
<p>UserID: {{.UserID}}</p>
<p>AccessToken: {{.AccessToken}}</p>
<p>ExpiresAt: {{.ExpiresAt}}</p>
<p>RefreshToken: {{.RefreshToken}}</p>

View File

@ -58,7 +58,7 @@ All features of Sundown are supported, including:
// Cache is a good and a must-feature on static content, i.e "about page" or for a whole blog site.
func main() {
app := iris.New()
app.Logger().SetLevel("debug")
app.Get("/", cache.Handler(10*time.Second), writeMarkdown)
// saves its content on the first request and serves it instead of re-calculating the content.
// After 10 seconds it will be cleared and resetted.

View File

@ -1,9 +1,9 @@
DisablePathCorrection = false
EnablePathEscape = false
FireMethodNotAllowed = true
DisableBodyConsumptionOnUnmarshal = false
TimeFormat = "Mon, 01 Jan 2006 15:04:05 GMT"
Charset = "UTF-8"
[Other]
MyServerName = "iris"
DisablePathCorrection = false
EnablePathEscape = false
FireMethodNotAllowed = true
DisableBodyConsumptionOnUnmarshal = false
TimeFormat = "Mon, 01 Jan 2006 15:04:05 GMT"
Charset = "UTF-8"
[Other]
MyServerName = "iris"

View File

@ -1,6 +1,6 @@
DisablePathCorrection: false
EnablePathEscape: false
FireMethodNotAllowed: true
DisableBodyConsumptionOnUnmarshal: true
TimeFormat: Mon, 01 Jan 2006 15:04:05 GMT
DisablePathCorrection: false
EnablePathEscape: false
FireMethodNotAllowed: true
DisableBodyConsumptionOnUnmarshal: true
TimeFormat: Mon, 01 Jan 2006 15:04:05 GMT
Charset: UTF-8

View File

@ -1,3 +1,3 @@
body {
background-color: black;
}
body {
background-color: black;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,3 @@
body {
background-color: black;
}
body {
background-color: black;
}

View File

@ -1,14 +1,14 @@
<html>
<head>
<title>{{ .Page.Title }}</title>
</head>
<body>
<h1> Hello from index.html </h1>
<script src="/app.js"> </script>
</body>
<html>
<head>
<title>{{ .Page.Title }}</title>
</head>
<body>
<h1> Hello from index.html </h1>
<script src="/app.js"> </script>
</body>
</html>

View File

@ -1,3 +1,3 @@
body {
background-color: black;
}
body {
background-color: black;
}

View File

@ -1,14 +1,14 @@
<html>
<head>
<title>{{ .Page.Title }}</title>
</head>
<body>
<h1> Hello from index.html </h1>
<script src="/app.js"> </script>
</body>
<html>
<head>
<title>{{ .Page.Title }}</title>
</head>
<body>
<h1> Hello from index.html </h1>
<script src="/app.js"> </script>
</body>
</html>

View File

@ -1,2 +1,2 @@
A silly example for this issue: https://github.com/kataras/iris/issues/688#issuecomment-318828259.
A silly example for this issue: https://github.com/kataras/iris/issues/688#issuecomment-318828259.
However it seems useful and therefore is being included in the examples for everyone else.

View File

@ -1,19 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDAzCCAeugAwIBAgIJAPDsxtKV4v3uMA0GCSqGSIb3DQEBBQUAMBgxFjAUBgNV
BAMMDTEyNy4wLjAuMTo0NDMwHhcNMTYwNjI5MTMxMjU4WhcNMjYwNjI3MTMxMjU4
WjAYMRYwFAYDVQQDDA0xMjcuMC4wLjE6NDQzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA0KtAOHKrcbLwWJXgRX7XSFyu4HHHpSty4bliv8ET4sLJpbZH
XeVX05Foex7PnrurDP6e+0H5TgqqcpQM17/ZlFcyKrJcHSCgV0ZDB3Sb8RLQSLns
8a+MOSbn1WZ7TkC7d/cWlKmasQRHQ2V/cWlGooyKNEPoGaEz8MbY0wn2spyIJwsB
dciERC6317VTXbiZdoD8QbAsT+tBvEHM2m2A7B7PQmHNehtyFNbSV5uZNodvv1uv
ZTnDa6IqpjFLb1b2HNFgwmaVPmmkLuy1l9PN+o6/DUnXKKBrfPAx4JOlqTKEQpWs
pnfacTE3sWkkmOSSFltAXfkXIJFKdS/hy5J/KQIDAQABo1AwTjAdBgNVHQ4EFgQU
zr1df/c9+NyTpmyiQO8g3a8NswYwHwYDVR0jBBgwFoAUzr1df/c9+NyTpmyiQO8g
3a8NswYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEACG5shtMSDgCd
MNjOF+YmD+PX3Wy9J9zehgaDJ1K1oDvBbQFl7EOJl8lRMWITSws22Wxwh8UXVibL
sscKBp14dR3e7DdbwCVIX/JyrJyOaCfy2nNBdf1B06jYFsIHvP3vtBAb9bPNOTBQ
QE0Ztu9kCqgsmu0//sHuBEeA3d3E7wvDhlqRSxTLcLtgC1NSgkFvBw0JvwgpkX6s
M5WpSBZwZv8qpplxhFfqNy8Uf+xrpSW0pGfkHumehkQGC6/Ry7raganS0aHhDPK9
Z1bEJ2com1bFFAQsm9yIXrRVMGGCtihB2Au0Q4jpEjUbzWYM+ItZyvRAGRM6Qex6
s/jogMeRsw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDAzCCAeugAwIBAgIJAPDsxtKV4v3uMA0GCSqGSIb3DQEBBQUAMBgxFjAUBgNV
BAMMDTEyNy4wLjAuMTo0NDMwHhcNMTYwNjI5MTMxMjU4WhcNMjYwNjI3MTMxMjU4
WjAYMRYwFAYDVQQDDA0xMjcuMC4wLjE6NDQzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA0KtAOHKrcbLwWJXgRX7XSFyu4HHHpSty4bliv8ET4sLJpbZH
XeVX05Foex7PnrurDP6e+0H5TgqqcpQM17/ZlFcyKrJcHSCgV0ZDB3Sb8RLQSLns
8a+MOSbn1WZ7TkC7d/cWlKmasQRHQ2V/cWlGooyKNEPoGaEz8MbY0wn2spyIJwsB
dciERC6317VTXbiZdoD8QbAsT+tBvEHM2m2A7B7PQmHNehtyFNbSV5uZNodvv1uv
ZTnDa6IqpjFLb1b2HNFgwmaVPmmkLuy1l9PN+o6/DUnXKKBrfPAx4JOlqTKEQpWs
pnfacTE3sWkkmOSSFltAXfkXIJFKdS/hy5J/KQIDAQABo1AwTjAdBgNVHQ4EFgQU
zr1df/c9+NyTpmyiQO8g3a8NswYwHwYDVR0jBBgwFoAUzr1df/c9+NyTpmyiQO8g
3a8NswYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEACG5shtMSDgCd
MNjOF+YmD+PX3Wy9J9zehgaDJ1K1oDvBbQFl7EOJl8lRMWITSws22Wxwh8UXVibL
sscKBp14dR3e7DdbwCVIX/JyrJyOaCfy2nNBdf1B06jYFsIHvP3vtBAb9bPNOTBQ
QE0Ztu9kCqgsmu0//sHuBEeA3d3E7wvDhlqRSxTLcLtgC1NSgkFvBw0JvwgpkX6s
M5WpSBZwZv8qpplxhFfqNy8Uf+xrpSW0pGfkHumehkQGC6/Ry7raganS0aHhDPK9
Z1bEJ2com1bFFAQsm9yIXrRVMGGCtihB2Au0Q4jpEjUbzWYM+ItZyvRAGRM6Qex6
s/jogMeRsw==
-----END CERTIFICATE-----

View File

@ -1,27 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA0KtAOHKrcbLwWJXgRX7XSFyu4HHHpSty4bliv8ET4sLJpbZH
XeVX05Foex7PnrurDP6e+0H5TgqqcpQM17/ZlFcyKrJcHSCgV0ZDB3Sb8RLQSLns
8a+MOSbn1WZ7TkC7d/cWlKmasQRHQ2V/cWlGooyKNEPoGaEz8MbY0wn2spyIJwsB
dciERC6317VTXbiZdoD8QbAsT+tBvEHM2m2A7B7PQmHNehtyFNbSV5uZNodvv1uv
ZTnDa6IqpjFLb1b2HNFgwmaVPmmkLuy1l9PN+o6/DUnXKKBrfPAx4JOlqTKEQpWs
pnfacTE3sWkkmOSSFltAXfkXIJFKdS/hy5J/KQIDAQABAoIBAQDCd+bo9I0s8Fun
4z3Y5oYSDTZ5O/CY0O5GyXPrSzCSM4Cj7EWEj1mTdb9Ohv9tam7WNHHLrcd+4NfK
4ok5hLVs1vqM6h6IksB7taKATz+Jo0PzkzrsXvMqzERhEBo4aoGMIv2rXIkrEdas
S+pCsp8+nAWtAeBMCn0Slu65d16vQxwgfod6YZfvMKbvfhOIOShl9ejQ+JxVZcMw
Ti8sgvYmFUrdrEH3nCgptARwbx4QwlHGaw/cLGHdepfFsVaNQsEzc7m61fSO70m4
NYJv48ZgjOooF5AccbEcQW9IxxikwNc+wpFYy5vDGzrBwS5zLZQFpoyMWFhtWdjx
hbmNn1jlAoGBAPs0ZjqsfDrH5ja4dQIdu5ErOccsmoHfMMBftMRqNG5neQXEmoLc
Uz8WeQ/QDf302aTua6E9iSjd7gglbFukVwMndQ1Q8Rwxz10jkXfiE32lFnqK0csx
ltruU6hOeSGSJhtGWBuNrT93G2lmy23fSG6BqOzdU4rn/2GPXy5zaxM/AoGBANSm
/E96RcBUiI6rDVqKhY+7M1yjLB41JrErL9a0Qfa6kYnaXMr84pOqVN11IjhNNTgl
g1lwxlpXZcZh7rYu9b7EEMdiWrJDQV7OxLDHopqUWkQ+3MHwqs6CxchyCq7kv9Df
IKqat7Me6Cyeo0MqcW+UMxlCRBxKQ9jqC7hDfZuXAoGBAJmyS8ImerP0TtS4M08i
JfsCOY21qqs/hbKOXCm42W+be56d1fOvHngBJf0YzRbO0sNo5Q14ew04DEWLsCq5
+EsDv0hwd7VKfJd+BakV99ruQTyk5wutwaEeJK1bph12MD6L4aiqHJAyLeFldZ45
+TUzu8mA+XaJz+U/NXtUPvU9AoGBALtl9M+tdy6I0Fa50ujJTe5eEGNAwK5WNKTI
5D2XWNIvk/Yh4shXlux+nI8UnHV1RMMX++qkAYi3oE71GsKeG55jdk3fFQInVsJQ
APGw3FDRD8M4ip62ki+u+tEr/tIlcAyHtWfjNKO7RuubWVDlZFXqCiXmSdOMdsH/
bxiREW49AoGACWev/eOzBoQJCRN6EvU2OV0s3b6f1QsPvcaH0zc6bgbBFOGmJU8v
pXhD88tsu9exptLkGVoYZjR0n0QT/2Kkyu93jVDW/80P7VCz8DKYyAJDa4CVwZxO
MlobQSunSDKx/CCJhWkbytCyh1bngAtwSAYLXavYIlJbAzx6FvtAIw4=
-----END RSA PRIVATE KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA0KtAOHKrcbLwWJXgRX7XSFyu4HHHpSty4bliv8ET4sLJpbZH
XeVX05Foex7PnrurDP6e+0H5TgqqcpQM17/ZlFcyKrJcHSCgV0ZDB3Sb8RLQSLns
8a+MOSbn1WZ7TkC7d/cWlKmasQRHQ2V/cWlGooyKNEPoGaEz8MbY0wn2spyIJwsB
dciERC6317VTXbiZdoD8QbAsT+tBvEHM2m2A7B7PQmHNehtyFNbSV5uZNodvv1uv
ZTnDa6IqpjFLb1b2HNFgwmaVPmmkLuy1l9PN+o6/DUnXKKBrfPAx4JOlqTKEQpWs
pnfacTE3sWkkmOSSFltAXfkXIJFKdS/hy5J/KQIDAQABAoIBAQDCd+bo9I0s8Fun
4z3Y5oYSDTZ5O/CY0O5GyXPrSzCSM4Cj7EWEj1mTdb9Ohv9tam7WNHHLrcd+4NfK
4ok5hLVs1vqM6h6IksB7taKATz+Jo0PzkzrsXvMqzERhEBo4aoGMIv2rXIkrEdas
S+pCsp8+nAWtAeBMCn0Slu65d16vQxwgfod6YZfvMKbvfhOIOShl9ejQ+JxVZcMw
Ti8sgvYmFUrdrEH3nCgptARwbx4QwlHGaw/cLGHdepfFsVaNQsEzc7m61fSO70m4
NYJv48ZgjOooF5AccbEcQW9IxxikwNc+wpFYy5vDGzrBwS5zLZQFpoyMWFhtWdjx
hbmNn1jlAoGBAPs0ZjqsfDrH5ja4dQIdu5ErOccsmoHfMMBftMRqNG5neQXEmoLc
Uz8WeQ/QDf302aTua6E9iSjd7gglbFukVwMndQ1Q8Rwxz10jkXfiE32lFnqK0csx
ltruU6hOeSGSJhtGWBuNrT93G2lmy23fSG6BqOzdU4rn/2GPXy5zaxM/AoGBANSm
/E96RcBUiI6rDVqKhY+7M1yjLB41JrErL9a0Qfa6kYnaXMr84pOqVN11IjhNNTgl
g1lwxlpXZcZh7rYu9b7EEMdiWrJDQV7OxLDHopqUWkQ+3MHwqs6CxchyCq7kv9Df
IKqat7Me6Cyeo0MqcW+UMxlCRBxKQ9jqC7hDfZuXAoGBAJmyS8ImerP0TtS4M08i
JfsCOY21qqs/hbKOXCm42W+be56d1fOvHngBJf0YzRbO0sNo5Q14ew04DEWLsCq5
+EsDv0hwd7VKfJd+BakV99ruQTyk5wutwaEeJK1bph12MD6L4aiqHJAyLeFldZ45
+TUzu8mA+XaJz+U/NXtUPvU9AoGBALtl9M+tdy6I0Fa50ujJTe5eEGNAwK5WNKTI
5D2XWNIvk/Yh4shXlux+nI8UnHV1RMMX++qkAYi3oE71GsKeG55jdk3fFQInVsJQ
APGw3FDRD8M4ip62ki+u+tEr/tIlcAyHtWfjNKO7RuubWVDlZFXqCiXmSdOMdsH/
bxiREW49AoGACWev/eOzBoQJCRN6EvU2OV0s3b6f1QsPvcaH0zc6bgbBFOGmJU8v
pXhD88tsu9exptLkGVoYZjR0n0QT/2Kkyu93jVDW/80P7VCz8DKYyAJDa4CVwZxO
MlobQSunSDKx/CCJhWkbytCyh1bngAtwSAYLXavYIlJbAzx6FvtAIw4=
-----END RSA PRIVATE KEY-----

View File

@ -1,22 +1,22 @@
<!DOCTYPE html>
<head>
<meta charset="utf-8">
</head>
<body>
<form action="/form_action" method="post">
Username: <input type="text" name="Username" /> <br />
Mail: <input type="text" name="Mail" /> <br />
Select one or more: <br/>
<select multiple="multiple" name="mydata">
<option value='one'>One</option>
<option value='two'>Two</option>
<option value='three'>Three</option>
<option value='four'>Four</option>
</select>
<hr />
<input type="submit" value="Send data" />
</form>
</body>
</html>
<!DOCTYPE html>
<head>
<meta charset="utf-8">
</head>
<body>
<form action="/form_action" method="post">
Username: <input type="text" name="Username" /> <br />
Mail: <input type="text" name="Mail" /> <br />
Select one or more: <br/>
<select multiple="multiple" name="mydata">
<option value='one'>One</option>
<option value='two'>Two</option>
<option value='three'>Three</option>
<option value='four'>Four</option>
</select>
<hr />
<input type="submit" value="Send data" />
</form>
</body>
</html>

View File

@ -1,12 +1,12 @@
<html>
<head>
<title>Upload file</title>
</head>
<body>
<form enctype="multipart/form-data"
action="http://127.0.0.1:8080/upload" method="POST">
<input type="file" name="uploadfile" /> <input type="hidden"
name="token" value="{{.}}" /> <input type="submit" value="upload" />
</form>
</body>
</html>
<html>
<head>
<title>Upload file</title>
</head>
<body>
<form enctype="multipart/form-data"
action="http://127.0.0.1:8080/upload" method="POST">
<input type="file" name="uploadfile" /> <input type="hidden"
name="token" value="{{.}}" /> <input type="submit" value="upload" />
</form>
</body>
</html>

View File

@ -1,61 +1,61 @@
/* Bordered form */
form {
border: 3px solid #f1f1f1;
}
/* Full-width inputs */
input[type=text], input[type=password] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
/* Set a style for all buttons */
button {
background-color: #4CAF50;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
}
/* Add a hover effect for buttons */
button:hover {
opacity: 0.8;
}
/* Extra style for the cancel button (red) */
.cancelbtn {
width: auto;
padding: 10px 18px;
background-color: #f44336;
}
/* Center the container */
/* Add padding to containers */
.container {
padding: 16px;
}
/* The "Forgot password" text */
span.psw {
float: right;
padding-top: 16px;
}
/* Change styles for span and cancel button on extra small screens */
@media screen and (max-width: 300px) {
span.psw {
display: block;
float: none;
}
.cancelbtn {
width: 100%;
}
/* Bordered form */
form {
border: 3px solid #f1f1f1;
}
/* Full-width inputs */
input[type=text], input[type=password] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
/* Set a style for all buttons */
button {
background-color: #4CAF50;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
}
/* Add a hover effect for buttons */
button:hover {
opacity: 0.8;
}
/* Extra style for the cancel button (red) */
.cancelbtn {
width: auto;
padding: 10px 18px;
background-color: #f44336;
}
/* Center the container */
/* Add padding to containers */
.container {
padding: 16px;
}
/* The "Forgot password" text */
span.psw {
float: right;
padding-top: 16px;
}
/* Change styles for span and cancel button on extra small screens */
@media screen and (max-width: 300px) {
span.psw {
display: block;
float: none;
}
.cancelbtn {
width: 100%;
}
}

View File

@ -1,55 +1,55 @@
# View Models
There should be the view models, the structure that the client will be able to see.
Example:
```go
import (
"github.com/kataras/iris/_examples/mvc/login/datamodels"
"github.com/kataras/iris/context"
)
type User struct {
datamodels.User
}
func (m User) IsValid() bool {
/* do some checks and return true if it's valid... */
return m.ID > 0
}
```
Iris is able to convert any custom data Structure into an HTTP Response Dispatcher,
so theoretically, something like the following is permitted if it's really necessary;
```go
// Dispatch completes the `kataras/iris/mvc#Result` interface.
// Sends a `User` as a controlled http response.
// If its ID is zero or less then it returns a 404 not found error
// else it returns its json representation,
// (just like the controller's functions do for custom types by default).
//
// Don't overdo it, the application's logic should not be here.
// It's just one more step of validation before the response,
// simple checks can be added here.
//
// It's just a showcase,
// imagine the potentials this feature gives when designing a bigger application.
//
// This is called where the return value from a controller's method functions
// is type of `User`.
// For example the `controllers/user_controller.go#GetBy`.
func (m User) Dispatch(ctx context.Context) {
if !m.IsValid() {
ctx.NotFound()
return
}
ctx.JSON(m, context.JSON{Indent: " "})
}
```
However, we will use the "datamodels" as the only one models package because
User structure doesn't contain any sensitive data, clients are able to see all of its fields
# View Models
There should be the view models, the structure that the client will be able to see.
Example:
```go
import (
"github.com/kataras/iris/_examples/mvc/login/datamodels"
"github.com/kataras/iris/context"
)
type User struct {
datamodels.User
}
func (m User) IsValid() bool {
/* do some checks and return true if it's valid... */
return m.ID > 0
}
```
Iris is able to convert any custom data Structure into an HTTP Response Dispatcher,
so theoretically, something like the following is permitted if it's really necessary;
```go
// Dispatch completes the `kataras/iris/mvc#Result` interface.
// Sends a `User` as a controlled http response.
// If its ID is zero or less then it returns a 404 not found error
// else it returns its json representation,
// (just like the controller's functions do for custom types by default).
//
// Don't overdo it, the application's logic should not be here.
// It's just one more step of validation before the response,
// simple checks can be added here.
//
// It's just a showcase,
// imagine the potentials this feature gives when designing a bigger application.
//
// This is called where the return value from a controller's method functions
// is type of `User`.
// For example the `controllers/user_controller.go#GetBy`.
func (m User) Dispatch(ctx context.Context) {
if !m.IsValid() {
ctx.NotFound()
return
}
ctx.JSON(m, context.JSON{Indent: " "})
}
```
However, we will use the "datamodels" as the only one models package because
User structure doesn't contain any sensitive data, clients are able to see all of its fields
and we don't need any extra functionality or validation inside it.

View File

@ -1,15 +1,15 @@
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
<h3>{{.Message}}</h3>
<footer>
<h2>Sitemap</h2>
<a href="http://localhost:8080/user/register">/user/register</a><br/>
<a href="http://localhost:8080/user/login">/user/login</a><br/>
<a href="http://localhost:8080/user/logout">/user/logout</a><br/>
<a href="http://localhost:8080/user/me">/user/me</a><br/>
<h3>requires authentication</h3><br/>
<a href="http://localhost:8080/users">/users</a><br/>
<a href="http://localhost:8080/users/1">/users/{id}</a><br/>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
<h3>{{.Message}}</h3>
<footer>
<h2>Sitemap</h2>
<a href="http://localhost:8080/user/register">/user/register</a><br/>
<a href="http://localhost:8080/user/login">/user/login</a><br/>
<a href="http://localhost:8080/user/logout">/user/logout</a><br/>
<a href="http://localhost:8080/user/me">/user/me</a><br/>
<h3>requires authentication</h3><br/>
<a href="http://localhost:8080/users">/users</a><br/>
<a href="http://localhost:8080/users/1">/users/{id}</a><br/>
</footer>

View File

@ -1,12 +1,12 @@
<html>
<head>
<title>{{.Title}}</title>
<link rel="stylesheet" type="text/css" href="/public/css/site.css" />
</head>
<body>
{{ yield }}
</body>
<html>
<head>
<title>{{.Title}}</title>
<link rel="stylesheet" type="text/css" href="/public/css/site.css" />
</head>
<body>
{{ yield }}
</body>
</html>

View File

@ -1,11 +1,11 @@
<form action="/user/login" method="POST">
<div class="container">
<label><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="username" required>
<label><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required>
<button type="submit">Login</button>
</div>
<form action="/user/login" method="POST">
<div class="container">
<label><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="username" required>
<label><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required>
<button type="submit">Login</button>
</div>
</form>

View File

@ -1,3 +1,3 @@
<p>
Welcome back <strong>{{.User.Firstname}}</strong>!
<p>
Welcome back <strong>{{.User.Firstname}}</strong>!
</p>

View File

@ -1,14 +1,14 @@
<form action="/user/register" method="POST">
<div class="container">
<label><b>Firstname</b></label>
<input type="text" placeholder="Enter Firstname" name="firstname" required>
<label><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="username" required>
<label><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required>
<button type="submit">Register</button>
</div>
<form action="/user/register" method="POST">
<div class="container">
<label><b>Firstname</b></label>
<input type="text" placeholder="Enter Firstname" name="firstname" required>
<label><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="username" required>
<label><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required>
<button type="submit">Register</button>
</div>
</form>

View File

@ -1,20 +1,20 @@
# Domain Models
There should be the domain/business-level models.
Example:
```go
import "github.com/kataras/iris/_examples/mvc/overview/datamodels"
type Movie struct {
datamodels.Movie
}
func (m Movie) Validate() (Movie, error) {
/* do some checks and return an error if that Movie is not valid */
}
```
However, we will use the "datamodels" as the only one models package because
# Domain Models
There should be the domain/business-level models.
Example:
```go
import "github.com/kataras/iris/_examples/mvc/overview/datamodels"
type Movie struct {
datamodels.Movie
}
func (m Movie) Validate() (Movie, error) {
/* do some checks and return an error if that Movie is not valid */
}
```
However, we will use the "datamodels" as the only one models package because
Movie structure we don't need any extra functionality or validation inside it.

View File

@ -1,3 +1,3 @@
# Repositories
# Repositories
The package which has direct access to the "datasource" and can manipulate data directly.

View File

@ -1,3 +1,3 @@
# Service Layer
# Service Layer
The package which has access to call functions from the "repositories" and "models" ("datamodels" only in that simple example). It should contain the domain logic.

View File

@ -1,55 +1,55 @@
# View Models
There should be the view models, the structure that the client will be able to see.
Example:
```go
import (
"github.com/kataras/iris/_examples/mvc/overview/datamodels"
"github.com/kataras/iris/context"
)
type Movie struct {
datamodels.Movie
}
func (m Movie) IsValid() bool {
/* do some checks and return true if it's valid... */
return m.ID > 0
}
```
Iris is able to convert any custom data Structure into an HTTP Response Dispatcher,
so theoretically, something like the following is permitted if it's really necessary;
```go
// Dispatch completes the `kataras/iris/mvc#Result` interface.
// Sends a `Movie` as a controlled http response.
// If its ID is zero or less then it returns a 404 not found error
// else it returns its json representation,
// (just like the controller's functions do for custom types by default).
//
// Don't overdo it, the application's logic should not be here.
// It's just one more step of validation before the response,
// simple checks can be added here.
//
// It's just a showcase,
// imagine the potentials this feature gives when designing a bigger application.
//
// This is called where the return value from a controller's method functions
// is type of `Movie`.
// For example the `controllers/movie_controller.go#GetBy`.
func (m Movie) Dispatch(ctx context.Context) {
if !m.IsValid() {
ctx.NotFound()
return
}
ctx.JSON(m, context.JSON{Indent: " "})
}
```
However, we will use the "datamodels" as the only one models package because
Movie structure doesn't contain any sensitive data, clients are able to see all of its fields
# View Models
There should be the view models, the structure that the client will be able to see.
Example:
```go
import (
"github.com/kataras/iris/_examples/mvc/overview/datamodels"
"github.com/kataras/iris/context"
)
type Movie struct {
datamodels.Movie
}
func (m Movie) IsValid() bool {
/* do some checks and return true if it's valid... */
return m.ID > 0
}
```
Iris is able to convert any custom data Structure into an HTTP Response Dispatcher,
so theoretically, something like the following is permitted if it's really necessary;
```go
// Dispatch completes the `kataras/iris/mvc#Result` interface.
// Sends a `Movie` as a controlled http response.
// If its ID is zero or less then it returns a 404 not found error
// else it returns its json representation,
// (just like the controller's functions do for custom types by default).
//
// Don't overdo it, the application's logic should not be here.
// It's just one more step of validation before the response,
// simple checks can be added here.
//
// It's just a showcase,
// imagine the potentials this feature gives when designing a bigger application.
//
// This is called where the return value from a controller's method functions
// is type of `Movie`.
// For example the `controllers/movie_controller.go#GetBy`.
func (m Movie) Dispatch(ctx context.Context) {
if !m.IsValid() {
ctx.NotFound()
return
}
ctx.JSON(m, context.JSON{Indent: " "})
}
```
However, we will use the "datamodels" as the only one models package because
Movie structure doesn't contain any sensitive data, clients are able to see all of its fields
and we don't need any extra functionality or validation inside it.

View File

@ -1,12 +1,12 @@
<!-- file: web/views/hello/index.html -->
<html>
<head>
<title>{{.Title}} - My App</title>
</head>
<body>
<p>{{.MyMessage}}</p>
</body>
<!-- file: web/views/hello/index.html -->
<html>
<head>
<title>{{.Title}} - My App</title>
</head>
<body>
<p>{{.MyMessage}}</p>
</body>
</html>

View File

@ -1,12 +1,12 @@
<!-- file: web/views/hello/name.html -->
<html>
<head>
<title>{{.}}' Portfolio - My App</title>
</head>
<body>
<h1>Hello {{.}}</h1>
</body>
<!-- file: web/views/hello/name.html -->
<html>
<head>
<title>{{.}}' Portfolio - My App</title>
</head>
<body>
<h1>Hello {{.}}</h1>
</body>
</html>

View File

@ -1,22 +1,22 @@
<html>
<head><title>Create verification</title></head>
<body>
<h1> Create Verification </h1>
<table style="width:550px">
<tr>
<th>Username</th>
<th>Firstname</th>
<th>Lastname</th>
<th>City</th>
<th>Age</th>
</tr>
<tr>
<td>{{ .Username }}</td>
<td>{{ .Firstname }}</td>
<td>{{ .Lastname }}</td>
<td>{{ .City }}</td>
<td>{{ .Age }}</td>
</tr>
</table>
</body>
</html>
<html>
<head><title>Create verification</title></head>
<body>
<h1> Create Verification </h1>
<table style="width:550px">
<tr>
<th>Username</th>
<th>Firstname</th>
<th>Lastname</th>
<th>City</th>
<th>Age</th>
</tr>
<tr>
<td>{{ .Username }}</td>
<td>{{ .Firstname }}</td>
<td>{{ .Lastname }}</td>
<td>{{ .City }}</td>
<td>{{ .Age }}</td>
</tr>
</table>
</body>
</html>

View File

@ -1,7 +1,7 @@
<html>
<head><title>Profile page</title></head>
<body>
<h1> Profile </h1>
<b> {{ .Username }} </b>
</body>
</html>
<html>
<head><title>Profile page</title></head>
<body>
<h1> Profile </h1>
<b> {{ .Username }} </b>
</body>
</html>

View File

@ -1,3 +1,3 @@
body {
background-color: black;
}
body {
background-color: black;
}

View File

@ -1,14 +1,14 @@
<html>
<head>
<title>{{ .Page.Title }}</title>
</head>
<body>
<h1> Hello from index.html </h1>
<script src="/app.js"> </script>
</body>
<html>
<head>
<title>{{ .Page.Title }}</title>
</head>
<body>
<h1> Hello from index.html </h1>
<script src="/app.js"> </script>
</body>
</html>

View File

@ -14,8 +14,6 @@ func main() {
if err != nil {
panic(err)
}
// use different go routines to sync the database
db.Async(true)
// close and unlock the database when control+C/cmd+C pressed
iris.RegisterOnInterrupt(func() {
@ -44,7 +42,7 @@ func main() {
s.Set("name", "iris")
//test if setted here
ctx.Writef("All ok session setted to: %s", s.GetString("name"))
ctx.Writef("All ok session value of the 'name' is: %s", s.GetString("name"))
})
app.Get("/set/{key}/{value}", func(ctx iris.Context) {
@ -54,14 +52,14 @@ func main() {
s.Set(key, value)
// test if setted here
ctx.Writef("All ok session setted to: %s", s.GetString(key))
ctx.Writef("All ok session value of the '%s' is: %s", key, s.GetString(key))
})
app.Get("/get", func(ctx iris.Context) {
// get a specific key, as string, if no found returns just an empty string
name := sess.Start(ctx).GetString("name")
ctx.Writef("The name on the /set was: %s", name)
ctx.Writef("The 'name' on the /set was: %s", name)
})
app.Get("/get/{key}", func(ctx iris.Context) {
@ -91,5 +89,5 @@ func main() {
sess.ShiftExpiration(ctx)
})
app.Run(iris.Addr(":8080"))
app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithoutVersionChecker)
}

View File

@ -11,8 +11,6 @@ import (
func main() {
db, _ := boltdb.New("./sessions/sessions.db", 0666, "users")
// use different go routines to sync the database
db.Async(true)
// close and unlock the database when control+C/cmd+C pressed
iris.RegisterOnInterrupt(func() {

View File

@ -5,19 +5,19 @@ import (
"github.com/kataras/iris"
"github.com/gorilla/securecookie"
"github.com/kataras/iris/sessions"
"github.com/kataras/iris/sessions/sessiondb/file"
)
func main() {
db, _ := file.New("./sessions/", 0666)
// use different go routines to sync the database
db.Async(true)
db, _ := file.New("./sessions/", 0755)
sess := sessions.New(sessions.Config{
Cookie: "sessionscookieid",
Expires: 45 * time.Minute, // <=0 means unlimited life
Expires: 24 * time.Hour, // <=0 means unlimited life
Encoding: securecookie.New([]byte("C2O6J6oYTd0CBCNERkWZK8jGOXTXf9X2"),
[]byte("UTp6fJsicraGxA2cslELrrLX7msg5jfE")),
})
//
@ -84,5 +84,5 @@ func main() {
sess.ShiftExpiration(ctx)
})
app.Run(iris.Addr(":8080"))
app.Run(iris.Addr(":8080"), iris.WithoutVersionChecker)
}

View File

@ -12,9 +12,6 @@ import (
func main() {
db, _ := leveldb.New("./sessions/")
// use different go routines to sync the database
db.Async(true)
// close and unlock the database when control+C/cmd+C pressed
iris.RegisterOnInterrupt(func() {
db.Close()

View File

@ -22,8 +22,6 @@ func main() {
IdleTimeout: service.DefaultRedisIdleTimeout,
Prefix: ""}) // optionally configure the bridge between your redis server
// use go routines to query the database
db.Async(true)
// close connection when control+C/cmd+C
iris.RegisterOnInterrupt(func() {
db.Close()

View File

@ -91,6 +91,13 @@ const (
Favicon = "favicon.ico"
)
// Configure accepts configurations and runs them inside the Bootstraper's context.
func (b *Bootstrapper) Configure(cs ...Configurator) {
for _, c := range cs {
c(b)
}
}
// Bootstrap prepares our application.
//
// Returns itself.

View File

@ -6,15 +6,9 @@ import (
"github.com/kataras/iris/_examples/structuring/bootstrap/routes"
)
var app = bootstrap.New("Awesome App", "kataras2006@hotmail.com",
identity.Configure,
routes.Configure,
)
func init() {
app.Bootstrap()
}
func main() {
app := bootstrap.New("Awesome App", "kataras2006@hotmail.com")
app.Bootstrap()
app.Configure(identity.Configure, routes.Configure)
app.Listen(":8080")
}

View File

@ -1,5 +1,5 @@
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
<h3>{{.Err.status}}</h3>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
<h3>{{.Err.status}}</h3>
<h4>{{.Err.message}}</h4>

View File

@ -1,23 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<title>{{.Title}} - {{.AppName}}</title>
</head>
<body>
<div>
<!-- Render the current template here -->
{{ yield }}
<hr />
<footer>
<p>&copy; 2017 - {{.AppOwner}}</p>
</footer>
</div>
</body>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<title>{{.Title}} - {{.AppName}}</title>
</head>
<body>
<div>
<!-- Render the current template here -->
{{ yield }}
<hr />
<footer>
<p>&copy; 2017 - {{.AppOwner}}</p>
</footer>
</div>
</body>
</html>

View File

@ -1,61 +1,61 @@
/* Bordered form */
form {
border: 3px solid #f1f1f1;
}
/* Full-width inputs */
input[type=text], input[type=password] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
/* Set a style for all buttons */
button {
background-color: #4CAF50;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
}
/* Add a hover effect for buttons */
button:hover {
opacity: 0.8;
}
/* Extra style for the cancel button (red) */
.cancelbtn {
width: auto;
padding: 10px 18px;
background-color: #f44336;
}
/* Center the container */
/* Add padding to containers */
.container {
padding: 16px;
}
/* The "Forgot password" text */
span.psw {
float: right;
padding-top: 16px;
}
/* Change styles for span and cancel button on extra small screens */
@media screen and (max-width: 300px) {
span.psw {
display: block;
float: none;
}
.cancelbtn {
width: 100%;
}
/* Bordered form */
form {
border: 3px solid #f1f1f1;
}
/* Full-width inputs */
input[type=text], input[type=password] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
/* Set a style for all buttons */
button {
background-color: #4CAF50;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
}
/* Add a hover effect for buttons */
button:hover {
opacity: 0.8;
}
/* Extra style for the cancel button (red) */
.cancelbtn {
width: auto;
padding: 10px 18px;
background-color: #f44336;
}
/* Center the container */
/* Add padding to containers */
.container {
padding: 16px;
}
/* The "Forgot password" text */
span.psw {
float: right;
padding-top: 16px;
}
/* Change styles for span and cancel button on extra small screens */
@media screen and (max-width: 300px) {
span.psw {
display: block;
float: none;
}
.cancelbtn {
width: 100%;
}
}

View File

@ -1,4 +1,4 @@
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
<h3>{{.Message}}</h3>

View File

@ -1,12 +1,12 @@
<html>
<head>
<title>{{.Title}}</title>
<link rel="stylesheet" type="text/css" href="/public/css/site.css" />
</head>
<body>
{{ yield }}
</body>
<html>
<head>
<title>{{.Title}}</title>
<link rel="stylesheet" type="text/css" href="/public/css/site.css" />
</head>
<body>
{{ yield }}
</body>
</html>

View File

@ -1,11 +1,11 @@
<form action="/user/login" method="POST">
<div class="container">
<label><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="username" required>
<label><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required>
<button type="submit">Login</button>
</div>
<form action="/user/login" method="POST">
<div class="container">
<label><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="username" required>
<label><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required>
<button type="submit">Login</button>
</div>
</form>

View File

@ -1,3 +1,3 @@
<p>
Welcome back <strong>{{.User.Firstname}}</strong>!
<p>
Welcome back <strong>{{.User.Firstname}}</strong>!
</p>

View File

@ -1,3 +1,3 @@
<p>
User with ID <strong>{{.ID}}</strong> does not exist.
<p>
User with ID <strong>{{.ID}}</strong> does not exist.
</p>

View File

@ -1,14 +1,14 @@
<form action="/user/register" method="POST">
<div class="container">
<label><b>Firstname</b></label>
<input type="text" placeholder="Enter Firstname" name="firstname" required>
<label><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="username" required>
<label><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required>
<button type="submit">Register</button>
</div>
<form action="/user/register" method="POST">
<div class="container">
<label><b>Firstname</b></label>
<input type="text" placeholder="Enter Firstname" name="firstname" required>
<label><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="username" required>
<label><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required>
<button type="submit">Register</button>
</div>
</form>

View File

@ -1,28 +1,28 @@
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know
127.0.0.1 domain.local
127.0.0.1 system.domain.local
127.0.0.1 dashboard.domain.local
#-END iris-
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know
127.0.0.1 domain.local
127.0.0.1 system.domain.local
127.0.0.1 dashboard.domain.local
#-END iris-

View File

@ -1,27 +1,27 @@
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know
127.0.0.1 mydomain.com
127.0.0.1 admin.mydomain.com
#-END iris-
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know
127.0.0.1 mydomain.com
127.0.0.1 admin.mydomain.com
#-END iris-

View File

@ -1,30 +1,30 @@
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know
127.0.0.1 mydomain.com
127.0.0.1 username1.mydomain.com
127.0.0.1 username2.mydomain.com
127.0.0.1 username3.mydomain.com
127.0.0.1 username4.mydomain.com
127.0.0.1 username5.mydomain.com
#-END iris-
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know
127.0.0.1 mydomain.com
127.0.0.1 username1.mydomain.com
127.0.0.1 username2.mydomain.com
127.0.0.1 username3.mydomain.com
127.0.0.1 username4.mydomain.com
127.0.0.1 username5.mydomain.com
#-END iris-

View File

@ -1,25 +1,25 @@
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know
127.0.0.1 mydomain.com
127.0.0.1 www.mydomain.com
#-END iris-
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know
127.0.0.1 mydomain.com
127.0.0.1 www.mydomain.com
#-END iris-

View File

@ -1,9 +1,9 @@
example.com {
header / Server "Iris"
proxy / example.com:9091 # localhost:9091
}
api.example.com {
header / Server "Iris"
proxy / api.example.com:9092 # localhost:9092
example.com {
header / Server "Iris"
proxy / example.com:9091 # localhost:9091
}
api.example.com {
header / Server "Iris"
proxy / api.example.com:9092 # localhost:9092
}

View File

@ -1,24 +1,24 @@
# Caddy loves Iris
The `Caddyfile` shows how you can use caddy to listen on ports 80 & 443 and sit in front of iris webserver(s) that serving on a different port (9091 and 9092 in this case; see Caddyfile).
## Running our two web servers
1. Go to `$GOPATH/src/github.com/kataras/iris/_examples/tutorial/caddy/server1`
2. Open a terminal window and execute `go run main.go`
3. Go to `$GOPATH/src/github.com/kataras/iris/_examples/tutorial/caddy/server2`
4. Open a new terminal window and execute `go run main.go`
## Caddy installation
1. Download caddy: https://caddyserver.com/download
2. Extract its contents where the `Caddyfile` is located, the `$GOPATH/src/github.com/kataras/iris/_examples/tutorial/caddy` in this case
3. Open, read and modify the `Caddyfile` to see by yourself how easy it is to configure the servers
4. Run `caddy` directly or open a terminal window and execute `caddy`
5. Go to `https://example.com` and `https://api.example.com/user/42`
## Notes
Iris has the `app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com"))` which does
# Caddy loves Iris
The `Caddyfile` shows how you can use caddy to listen on ports 80 & 443 and sit in front of iris webserver(s) that serving on a different port (9091 and 9092 in this case; see Caddyfile).
## Running our two web servers
1. Go to `$GOPATH/src/github.com/kataras/iris/_examples/tutorial/caddy/server1`
2. Open a terminal window and execute `go run main.go`
3. Go to `$GOPATH/src/github.com/kataras/iris/_examples/tutorial/caddy/server2`
4. Open a new terminal window and execute `go run main.go`
## Caddy installation
1. Download caddy: https://caddyserver.com/download
2. Extract its contents where the `Caddyfile` is located, the `$GOPATH/src/github.com/kataras/iris/_examples/tutorial/caddy` in this case
3. Open, read and modify the `Caddyfile` to see by yourself how easy it is to configure the servers
4. Run `caddy` directly or open a terminal window and execute `caddy`
5. Go to `https://example.com` and `https://api.example.com/user/42`
## Notes
Iris has the `app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com"))` which does
the exactly same thing but caddy is a great tool that helps you when you run multiple web servers from one host machine, i.e iris, apache, tomcat.

View File

@ -1,3 +1,3 @@
<div>
{{.Message}}
<div>
{{.Message}}
</div>

View File

@ -1,11 +1,11 @@
<html>
<head>
<title>{{.Layout.Title}}</title>
</head>
<body>
{{ yield }}
</body>
<html>
<head>
<title>{{.Layout.Title}}</title>
</head>
<body>
{{ yield }}
</body>
</html>

View File

@ -1,168 +1,168 @@
# Articles
- [How to build a file upload form using DropzoneJS and Go](https://dev.to/kataras/dropzonejs--go-series---part-1-474)
- [How to display existing files on server using DropzoneJS and Go](https://dev.to/kataras/dropzonejs--go-series---part-2-4n1)
# Content
This is the part 1 of 2 in DropzoneJS + Go series.
- [Part 1: How to build a file upload form](README.md)
- [Part 2: How to display existing files on server](README_PART2.md)
# DropzoneJS + Go: How to build a file upload form
[DropzoneJS](https://github.com/enyo/dropzone) is an open source library that provides drag'n'drop file uploads with image previews. It is a great JavaScript library which actually does not even rely on JQuery.
In this tutorial, we are building a multiple file upload form using DropzoneJS, and the backend will be handled by Go and [Iris](https://iris-go.com).
## Table Of Content
- [Preparation](#preparation)
- [Work with DropzoneJS](#work-with-dropzonejs)
- [Work with Go](#work-with-go)
## Preparation
1. Download [Go(Golang)](https://golang.org/dl), setup your computer as shown there and continue to 2.
2. Install [Iris](https://github.com/kataras/iris); open a terminal and execute `go get -u github.com/kataras/iris`
3. Download DropzoneJS from [this URL](https://raw.githubusercontent.com/enyo/dropzone/master/dist/dropzone.js). DropzoneJS does not rely on JQuery, you will not have to worry that, upgrading JQuery version breaks your application.
4. Download dropzone.css from [this URL](https://raw.githubusercontent.com/enyo/dropzone/master/dist/dropzone.css), if you want some already made css.
5. Create a folder "./public/uploads", this is for storing uploaded files.
6. Create a file "./views/upload.html", this is for the front form page.
7. Create a file "./main.go", this is for handling backend file upload process.
Your folder&file structure should look like this after the preparation:
![folder&file structure](folder_structure.png)
## Work with DropzoneJS
Open file "./views/upload.html" and let us create a DropzoneJs form.
Copy the content below to "./views/upload.html" and we will go through each line of code individually.
```html
<!-- /views/upload.html -->
<html>
<head>
<title>DropzoneJS Uploader</title>
<!-- 1 -->
<link href="/public/css/dropzone.css" type="text/css" rel="stylesheet" />
<!-- 2 -->
<script src="/public/js/dropzone.js"></script>
</head>
<body>
<!-- 3 -->
<form action="/upload" method="POST" class="dropzone" id="my-dropzone">
<div class="fallback">
<input name="file" type="file" multiple />
<input type="submit" value="Upload" />
</div>
</form>
</body>
</html>
```
1. Include the CSS Stylesheet.
2. Include DropzoneJS JavaScript library.
3. Create an upload form with css class "dropzone" and "action" is the route path "/upload". Note that we did create an input filed for fallback mode. This is all handled by DropzoneJS library itself. All we need to do is assign css class "dropzone" to the form. By default, DropzoneJS will find all forms with class "dropzone" and automatically attach itself to it.
## Work with Go
Now you have come to Last part of the tutorial. In this section, we will store files sent from DropzoneJS to the "./public/uploads" folder.
Open "main.go" and copy the code below:
```go
// main.go
package main
import (
"os"
"io"
"strings"
"github.com/kataras/iris"
)
const uploadsDir = "./public/uploads/"
func main() {
app := iris.New()
// Register templates
app.RegisterView(iris.HTML("./views", ".html"))
// Make the /public route path to statically serve the ./public/... contents
app.StaticWeb("/public", "./public")
// Render the actual form
// GET: http://localhost:8080
app.Get("/", func(ctx iris.Context) {
ctx.View("upload.html")
})
// Upload the file to the server
// POST: http://localhost:8080/upload
app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) {
// Get the file from the dropzone request
file, info, err := ctx.FormFile("file")
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.Application().Logger().Warnf("Error while uploading: %v", err.Error())
return
}
defer file.Close()
fname := info.Filename
// Create a file with the same name
// assuming that you have a folder named 'uploads'
out, err := os.OpenFile(uploadsDir+fname,
os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.Application().Logger().Warnf("Error while preparing the new file: %v", err.Error())
return
}
defer out.Close()
io.Copy(out, file)
})
// Start the server at http://localhost:8080
app.Run(iris.Addr(":8080"))
}
```
1. Create a new Iris app.
2. Register and load templates from the "views" folder.
3. Make the "/public" route path to statically serve the ./public/... folder's contents
4. Create a route to serve the upload form.
5. Create a route to handle the POST form data from the DropzoneJS' form
6. Declare a variable for destination folder.
7. If file is sent to the page, store the file object to a temporary "file" variable.
8. Move uploaded file to destination based on the uploadsDir+uploaded file's name.
### Running the server
Open the terminal at the current project's folder and execute:
```bash
$ go run main.go
Now listening on: http://localhost:8080
Application started. Press CTRL+C to shut down.
```
Now go to browser, and navigate to http://localhost:8080, you should be able to see a page as below:
![no files screenshot](no_files.png)
# Articles
- [How to build a file upload form using DropzoneJS and Go](https://dev.to/kataras/dropzonejs--go-series---part-1-474)
- [How to display existing files on server using DropzoneJS and Go](https://dev.to/kataras/dropzonejs--go-series---part-2-4n1)
# Content
This is the part 1 of 2 in DropzoneJS + Go series.
- [Part 1: How to build a file upload form](README.md)
- [Part 2: How to display existing files on server](README_PART2.md)
# DropzoneJS + Go: How to build a file upload form
[DropzoneJS](https://github.com/enyo/dropzone) is an open source library that provides drag'n'drop file uploads with image previews. It is a great JavaScript library which actually does not even rely on JQuery.
In this tutorial, we are building a multiple file upload form using DropzoneJS, and the backend will be handled by Go and [Iris](https://iris-go.com).
## Table Of Content
- [Preparation](#preparation)
- [Work with DropzoneJS](#work-with-dropzonejs)
- [Work with Go](#work-with-go)
## Preparation
1. Download [Go(Golang)](https://golang.org/dl), setup your computer as shown there and continue to 2.
2. Install [Iris](https://github.com/kataras/iris); open a terminal and execute `go get -u github.com/kataras/iris`
3. Download DropzoneJS from [this URL](https://raw.githubusercontent.com/enyo/dropzone/master/dist/dropzone.js). DropzoneJS does not rely on JQuery, you will not have to worry that, upgrading JQuery version breaks your application.
4. Download dropzone.css from [this URL](https://raw.githubusercontent.com/enyo/dropzone/master/dist/dropzone.css), if you want some already made css.
5. Create a folder "./public/uploads", this is for storing uploaded files.
6. Create a file "./views/upload.html", this is for the front form page.
7. Create a file "./main.go", this is for handling backend file upload process.
Your folder&file structure should look like this after the preparation:
![folder&file structure](folder_structure.png)
## Work with DropzoneJS
Open file "./views/upload.html" and let us create a DropzoneJs form.
Copy the content below to "./views/upload.html" and we will go through each line of code individually.
```html
<!-- /views/upload.html -->
<html>
<head>
<title>DropzoneJS Uploader</title>
<!-- 1 -->
<link href="/public/css/dropzone.css" type="text/css" rel="stylesheet" />
<!-- 2 -->
<script src="/public/js/dropzone.js"></script>
</head>
<body>
<!-- 3 -->
<form action="/upload" method="POST" class="dropzone" id="my-dropzone">
<div class="fallback">
<input name="file" type="file" multiple />
<input type="submit" value="Upload" />
</div>
</form>
</body>
</html>
```
1. Include the CSS Stylesheet.
2. Include DropzoneJS JavaScript library.
3. Create an upload form with css class "dropzone" and "action" is the route path "/upload". Note that we did create an input filed for fallback mode. This is all handled by DropzoneJS library itself. All we need to do is assign css class "dropzone" to the form. By default, DropzoneJS will find all forms with class "dropzone" and automatically attach itself to it.
## Work with Go
Now you have come to Last part of the tutorial. In this section, we will store files sent from DropzoneJS to the "./public/uploads" folder.
Open "main.go" and copy the code below:
```go
// main.go
package main
import (
"os"
"io"
"strings"
"github.com/kataras/iris"
)
const uploadsDir = "./public/uploads/"
func main() {
app := iris.New()
// Register templates
app.RegisterView(iris.HTML("./views", ".html"))
// Make the /public route path to statically serve the ./public/... contents
app.StaticWeb("/public", "./public")
// Render the actual form
// GET: http://localhost:8080
app.Get("/", func(ctx iris.Context) {
ctx.View("upload.html")
})
// Upload the file to the server
// POST: http://localhost:8080/upload
app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) {
// Get the file from the dropzone request
file, info, err := ctx.FormFile("file")
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.Application().Logger().Warnf("Error while uploading: %v", err.Error())
return
}
defer file.Close()
fname := info.Filename
// Create a file with the same name
// assuming that you have a folder named 'uploads'
out, err := os.OpenFile(uploadsDir+fname,
os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.Application().Logger().Warnf("Error while preparing the new file: %v", err.Error())
return
}
defer out.Close()
io.Copy(out, file)
})
// Start the server at http://localhost:8080
app.Run(iris.Addr(":8080"))
}
```
1. Create a new Iris app.
2. Register and load templates from the "views" folder.
3. Make the "/public" route path to statically serve the ./public/... folder's contents
4. Create a route to serve the upload form.
5. Create a route to handle the POST form data from the DropzoneJS' form
6. Declare a variable for destination folder.
7. If file is sent to the page, store the file object to a temporary "file" variable.
8. Move uploaded file to destination based on the uploadsDir+uploaded file's name.
### Running the server
Open the terminal at the current project's folder and execute:
```bash
$ go run main.go
Now listening on: http://localhost:8080
Application started. Press CTRL+C to shut down.
```
Now go to browser, and navigate to http://localhost:8080, you should be able to see a page as below:
![no files screenshot](no_files.png)
![with uploaded files screenshot](with_files.png)

View File

@ -1,310 +1,310 @@
# Articles
- [How to build a file upload form using DropzoneJS and Go](https://dev.to/kataras/dropzonejs--go-series---part-1-474)
- [How to display existing files on server using DropzoneJS and Go](https://dev.to/kataras/dropzonejs--go-series---part-2-4n1)
# Content
This is the part 2 of 2 in DropzoneJS + Go series.
- [Part 1: How to build a file upload form](README.md)
- [Part 2: How to display existing files on server](README_PART2.md)
# DropzoneJS + Go: How to display existing files on server
In this tutorial, we will show you how to display existing files on the server when using DropzoneJS and Go. This tutorial is based on [How to build a file upload form using DropzoneJS and Go](README.md). Make sure you have read it before proceeding to content in this tutorial.
## Table Of Content
- [Preparation](#preparation)
- [Modify the Server side](#modify-the-server-side)
- [Modify the Client side](#modify-the-client-side)
- [References](#references)
- [The End](#the-end)
## Preparation
Install the go package "github.com/nfnt/resize" with `go get github.com/nfnt/resize`, we need it to create thumbnails.
In previous [tutorial](README.md). We have already set up a proper working DropzoneJs upload form. There is no additional file needed for this tutorial. What we need to do is to make some modifications to file below:
1. main.go
2. views/upload.html
Let us get started!
## Modify the Server side
In previous tutorial. All "/upload" does is to store uploaded files to the server directory "./public/uploads". So we need to add a piece of code to retrieve stored files' information (name and size), and return it in JSON format.
Copy the content below to "main.go". Read comments for details.
```go
// main.go
package main
import (
"image/jpeg"
"image/png"
"io"
"os"
"path"
"path/filepath"
"strings"
"sync"
"github.com/kataras/iris"
"github.com/nfnt/resize" // $ go get -u github.com/nfnt/resize
)
const uploadsDir = "./public/uploads/"
type uploadedFile struct {
// {name: "", size: } are the dropzone's only requirements.
Name string `json:"name"`
Size int64 `json:"size"`
}
type uploadedFiles struct {
dir string
items []uploadedFile
mu sync.RWMutex // slices are safe but RWMutex is a good practise for you.
}
// scan the ./public/uploads folder for any files
// add them to a new uploadedFiles list.
func scanUploads(dir string) *uploadedFiles {
f := new(uploadedFiles)
lindex := dir[len(dir)-1]
if lindex != os.PathSeparator && lindex != '/' {
dir += string(os.PathSeparator)
}
// create directories if necessary
// and if, then return empty uploaded files; skipping the scan.
if err := os.MkdirAll(dir, os.FileMode(0666)); err != nil {
return f
}
// otherwise scan the given "dir" for files.
f.scan(dir)
return f
}
func (f *uploadedFiles) scan(dir string) {
f.dir = dir
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
// if it's directory or a thumbnail we saved earlier, skip it.
if info.IsDir() || strings.HasPrefix(info.Name(), "thumbnail_") {
return nil
}
f.add(info.Name(), info.Size())
return nil
})
}
// add the file's Name and Size to the uploadedFiles memory list
func (f *uploadedFiles) add(name string, size int64) uploadedFile {
uf := uploadedFile{
Name: name,
Size: size,
}
f.mu.Lock()
f.items = append(f.items, uf)
f.mu.Unlock()
return uf
}
// create thumbnail 100x100
// and save that to the ./public/uploads/thumbnail_$FILENAME
func (f *uploadedFiles) createThumbnail(uf uploadedFile) {
file, err := os.Open(path.Join(f.dir, uf.Name))
if err != nil {
return
}
defer file.Close()
name := strings.ToLower(uf.Name)
out, err := os.OpenFile(f.dir+"thumbnail_"+uf.Name,
os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return
}
defer out.Close()
if strings.HasSuffix(name, ".jpg") {
// decode jpeg into image.Image
img, err := jpeg.Decode(file)
if err != nil {
return
}
// write new image to file
resized := resize.Thumbnail(180, 180, img, resize.Lanczos3)
jpeg.Encode(out, resized,
&jpeg.Options{Quality: jpeg.DefaultQuality})
} else if strings.HasSuffix(name, ".png") {
img, err := png.Decode(file)
if err != nil {
return
}
// write new image to file
resized := resize.Thumbnail(180, 180, img, resize.Lanczos3) // slower but better res
png.Encode(out, resized)
}
// and so on... you got the point, this code can be simplify, as a practise.
}
func main() {
app := iris.New()
app.RegisterView(iris.HTML("./views", ".html"))
app.StaticWeb("/public", "./public")
app.Get("/", func(ctx iris.Context) {
ctx.View("upload.html")
})
files := scanUploads(uploadsDir)
app.Get("/uploads", func(ctx iris.Context) {
ctx.JSON(files.items)
})
app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) {
// Get the file from the dropzone request
file, info, err := ctx.FormFile("file")
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.Application().Logger().Warnf("Error while uploading: %v", err.Error())
return
}
defer file.Close()
fname := info.Filename
// Create a file with the same name
// assuming that you have a folder named 'uploads'
out, err := os.OpenFile(uploadsDir+fname,
os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.Application().Logger().Warnf("Error while preparing the new file: %v", err.Error())
return
}
defer out.Close()
io.Copy(out, file)
// optionally, add that file to the list in order to be visible when refresh.
uploadedFile := files.add(fname, info.Size)
go files.createThumbnail(uploadedFile)
})
// start the server at http://localhost:8080
app.Run(iris.Addr(":8080"))
}
```
## Modify the Client side
Copy content below to "./views/upload.html". We will go through modifications individually.
```html
<!-- /views/upload.html -->
<html>
<head>
<title>DropzoneJS Uploader</title>
<!-- 1 -->
<link href="/public/css/dropzone.css" type="text/css" rel="stylesheet" />
<!-- 2 -->
<script src="/public/js/dropzone.js"></script>
<!-- 4 -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<!-- 5 -->
<script>
Dropzone.options.myDropzone = {
paramName: "file", // The name that will be used to transfer the file
init: function () {
thisDropzone = this;
// 6
$.get('/uploads', function (data) {
if (data == null) {
return;
}
// 7
$.each(data, function (key, value) {
var mockFile = { name: value.name, size: value.size };
thisDropzone.emit("addedfile", mockFile);
thisDropzone.options.thumbnail.call(thisDropzone, mockFile, '/public/uploads/thumbnail_' + value.name);
// Make sure that there is no progress bar, etc...
thisDropzone.emit("complete", mockFile);
});
});
}
};
</script>
</head>
<body>
<!-- 3 -->
<form action="/upload" method="POST" class="dropzone" id="my-dropzone">
<div class="fallback">
<input name="file" type="file" multiple />
<input type="submit" value="Upload" />
</div>
</form>
</body>
</html>
```
1. We added Jquery library into our page. This actually not for DropzoneJs directly. We are using Jquery's ajax function **$.get** only. You will see below
2. We added an ID element (my-dropzone) to the form. This is needed because we need to pass configuration values to Dropzone. And to do it, we must have an ID reference of it. So that we can configure it by assigning values to Dropzone.options.myDropzone. A lot of people face confusion when configuring Dropzone. To put it in a simple way. Do not take Dropzone as a Jquery plugin, it has its own syntax and you need to follow it.
3. This starts the main part of modification. What we did here is to pass a function to listen to Dropzone's init event. This event is called when Dropzone is initialized.
4. Retrieve files details from the new "/uploads" via ajax.
5. Create mockFile using values from server. mockFile is simply JavaScript objects with properties of name and size. Then we call Dropzone's **addedfile** and **thumbnail** functions explicitly to put existing files to Dropzone upload area and generate its thumbnail.
### Running the server
Open the terminal at the current project's folder and execute:
```bash
$ go run main.go
Now listening on: http://localhost:8080
Application started. Press CTRL+C to shut down.
```
If you have done it successfully. Now go and upload some images and reload the upload page. Already uploaded files should auto display in Dropzone area.
![with uploaded files screenshot](with_files.png)
## References
- http://www.dropzonejs.com/#server-side-implementation
- https://www.startutorial.com/articles/view/how-to-build-a-file-upload-form-using-dropzonejs-and-php
- https://docs.iris-go.com
- https://github.com/kataras/iris/tree/master/_examples/tutorial/dropzonejs
## The end
Hopefully this simple tutorial helped you with your development.
# Articles
- [How to build a file upload form using DropzoneJS and Go](https://dev.to/kataras/dropzonejs--go-series---part-1-474)
- [How to display existing files on server using DropzoneJS and Go](https://dev.to/kataras/dropzonejs--go-series---part-2-4n1)
# Content
This is the part 2 of 2 in DropzoneJS + Go series.
- [Part 1: How to build a file upload form](README.md)
- [Part 2: How to display existing files on server](README_PART2.md)
# DropzoneJS + Go: How to display existing files on server
In this tutorial, we will show you how to display existing files on the server when using DropzoneJS and Go. This tutorial is based on [How to build a file upload form using DropzoneJS and Go](README.md). Make sure you have read it before proceeding to content in this tutorial.
## Table Of Content
- [Preparation](#preparation)
- [Modify the Server side](#modify-the-server-side)
- [Modify the Client side](#modify-the-client-side)
- [References](#references)
- [The End](#the-end)
## Preparation
Install the go package "github.com/nfnt/resize" with `go get github.com/nfnt/resize`, we need it to create thumbnails.
In previous [tutorial](README.md). We have already set up a proper working DropzoneJs upload form. There is no additional file needed for this tutorial. What we need to do is to make some modifications to file below:
1. main.go
2. views/upload.html
Let us get started!
## Modify the Server side
In previous tutorial. All "/upload" does is to store uploaded files to the server directory "./public/uploads". So we need to add a piece of code to retrieve stored files' information (name and size), and return it in JSON format.
Copy the content below to "main.go". Read comments for details.
```go
// main.go
package main
import (
"image/jpeg"
"image/png"
"io"
"os"
"path"
"path/filepath"
"strings"
"sync"
"github.com/kataras/iris"
"github.com/nfnt/resize" // $ go get -u github.com/nfnt/resize
)
const uploadsDir = "./public/uploads/"
type uploadedFile struct {
// {name: "", size: } are the dropzone's only requirements.
Name string `json:"name"`
Size int64 `json:"size"`
}
type uploadedFiles struct {
dir string
items []uploadedFile
mu sync.RWMutex // slices are safe but RWMutex is a good practise for you.
}
// scan the ./public/uploads folder for any files
// add them to a new uploadedFiles list.
func scanUploads(dir string) *uploadedFiles {
f := new(uploadedFiles)
lindex := dir[len(dir)-1]
if lindex != os.PathSeparator && lindex != '/' {
dir += string(os.PathSeparator)
}
// create directories if necessary
// and if, then return empty uploaded files; skipping the scan.
if err := os.MkdirAll(dir, os.FileMode(0666)); err != nil {
return f
}
// otherwise scan the given "dir" for files.
f.scan(dir)
return f
}
func (f *uploadedFiles) scan(dir string) {
f.dir = dir
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
// if it's directory or a thumbnail we saved earlier, skip it.
if info.IsDir() || strings.HasPrefix(info.Name(), "thumbnail_") {
return nil
}
f.add(info.Name(), info.Size())
return nil
})
}
// add the file's Name and Size to the uploadedFiles memory list
func (f *uploadedFiles) add(name string, size int64) uploadedFile {
uf := uploadedFile{
Name: name,
Size: size,
}
f.mu.Lock()
f.items = append(f.items, uf)
f.mu.Unlock()
return uf
}
// create thumbnail 100x100
// and save that to the ./public/uploads/thumbnail_$FILENAME
func (f *uploadedFiles) createThumbnail(uf uploadedFile) {
file, err := os.Open(path.Join(f.dir, uf.Name))
if err != nil {
return
}
defer file.Close()
name := strings.ToLower(uf.Name)
out, err := os.OpenFile(f.dir+"thumbnail_"+uf.Name,
os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return
}
defer out.Close()
if strings.HasSuffix(name, ".jpg") {
// decode jpeg into image.Image
img, err := jpeg.Decode(file)
if err != nil {
return
}
// write new image to file
resized := resize.Thumbnail(180, 180, img, resize.Lanczos3)
jpeg.Encode(out, resized,
&jpeg.Options{Quality: jpeg.DefaultQuality})
} else if strings.HasSuffix(name, ".png") {
img, err := png.Decode(file)
if err != nil {
return
}
// write new image to file
resized := resize.Thumbnail(180, 180, img, resize.Lanczos3) // slower but better res
png.Encode(out, resized)
}
// and so on... you got the point, this code can be simplify, as a practise.
}
func main() {
app := iris.New()
app.RegisterView(iris.HTML("./views", ".html"))
app.StaticWeb("/public", "./public")
app.Get("/", func(ctx iris.Context) {
ctx.View("upload.html")
})
files := scanUploads(uploadsDir)
app.Get("/uploads", func(ctx iris.Context) {
ctx.JSON(files.items)
})
app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) {
// Get the file from the dropzone request
file, info, err := ctx.FormFile("file")
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.Application().Logger().Warnf("Error while uploading: %v", err.Error())
return
}
defer file.Close()
fname := info.Filename
// Create a file with the same name
// assuming that you have a folder named 'uploads'
out, err := os.OpenFile(uploadsDir+fname,
os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.Application().Logger().Warnf("Error while preparing the new file: %v", err.Error())
return
}
defer out.Close()
io.Copy(out, file)
// optionally, add that file to the list in order to be visible when refresh.
uploadedFile := files.add(fname, info.Size)
go files.createThumbnail(uploadedFile)
})
// start the server at http://localhost:8080
app.Run(iris.Addr(":8080"))
}
```
## Modify the Client side
Copy content below to "./views/upload.html". We will go through modifications individually.
```html
<!-- /views/upload.html -->
<html>
<head>
<title>DropzoneJS Uploader</title>
<!-- 1 -->
<link href="/public/css/dropzone.css" type="text/css" rel="stylesheet" />
<!-- 2 -->
<script src="/public/js/dropzone.js"></script>
<!-- 4 -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<!-- 5 -->
<script>
Dropzone.options.myDropzone = {
paramName: "file", // The name that will be used to transfer the file
init: function () {
thisDropzone = this;
// 6
$.get('/uploads', function (data) {
if (data == null) {
return;
}
// 7
$.each(data, function (key, value) {
var mockFile = { name: value.name, size: value.size };
thisDropzone.emit("addedfile", mockFile);
thisDropzone.options.thumbnail.call(thisDropzone, mockFile, '/public/uploads/thumbnail_' + value.name);
// Make sure that there is no progress bar, etc...
thisDropzone.emit("complete", mockFile);
});
});
}
};
</script>
</head>
<body>
<!-- 3 -->
<form action="/upload" method="POST" class="dropzone" id="my-dropzone">
<div class="fallback">
<input name="file" type="file" multiple />
<input type="submit" value="Upload" />
</div>
</form>
</body>
</html>
```
1. We added Jquery library into our page. This actually not for DropzoneJs directly. We are using Jquery's ajax function **$.get** only. You will see below
2. We added an ID element (my-dropzone) to the form. This is needed because we need to pass configuration values to Dropzone. And to do it, we must have an ID reference of it. So that we can configure it by assigning values to Dropzone.options.myDropzone. A lot of people face confusion when configuring Dropzone. To put it in a simple way. Do not take Dropzone as a Jquery plugin, it has its own syntax and you need to follow it.
3. This starts the main part of modification. What we did here is to pass a function to listen to Dropzone's init event. This event is called when Dropzone is initialized.
4. Retrieve files details from the new "/uploads" via ajax.
5. Create mockFile using values from server. mockFile is simply JavaScript objects with properties of name and size. Then we call Dropzone's **addedfile** and **thumbnail** functions explicitly to put existing files to Dropzone upload area and generate its thumbnail.
### Running the server
Open the terminal at the current project's folder and execute:
```bash
$ go run main.go
Now listening on: http://localhost:8080
Application started. Press CTRL+C to shut down.
```
If you have done it successfully. Now go and upload some images and reload the upload page. Already uploaded files should auto display in Dropzone area.
![with uploaded files screenshot](with_files.png)
## References
- http://www.dropzonejs.com/#server-side-implementation
- https://www.startutorial.com/articles/view/how-to-build-a-file-upload-form-using-dropzonejs-and-php
- https://docs.iris-go.com
- https://github.com/kataras/iris/tree/master/_examples/tutorial/dropzonejs
## The end
Hopefully this simple tutorial helped you with your development.
If you like my post, please follow me on [Twitter](https://twitter.com/makismaropoulos) and help spread the word. I need your support to continue.

View File

@ -1,53 +1,53 @@
<html>
<head>
<title>DropzoneJS Uploader</title>
<!-- 1 -->
<link href="/public/css/dropzone.css" type="text/css" rel="stylesheet" />
<!-- 2 -->
<script src="/public/js/dropzone.js"></script>
<!-- 4 -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<!-- 5 -->
<script>
Dropzone.options.myDropzone = {
paramName: "file", // The name that will be used to transfer the file
init: function () {
thisDropzone = this;
// 6
$.get('/uploads', function (data) {
if (data == null) {
return;
}
// 7
$.each(data, function (key, value) {
var mockFile = { name: value.name, size: value.size };
thisDropzone.emit("addedfile", mockFile);
thisDropzone.options.thumbnail.call(thisDropzone, mockFile, '/public/uploads/thumbnail_' + value.name);
// thisDropzone.createThumbnailFromUrl(mockFile, '/public/uploads/' + value.name); <- doesn't work...
// Make sure that there is no progress bar, etc...
thisDropzone.emit("complete", mockFile);
});
});
}
};
</script>
</head>
<body>
<!-- 3 -->
<form action="/upload" method="POST" class="dropzone" id="my-dropzone">
<div class="fallback">
<input name="file" type="file" multiple />
<input type="submit" value="Upload" />
</div>
</form>
</body>
<html>
<head>
<title>DropzoneJS Uploader</title>
<!-- 1 -->
<link href="/public/css/dropzone.css" type="text/css" rel="stylesheet" />
<!-- 2 -->
<script src="/public/js/dropzone.js"></script>
<!-- 4 -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<!-- 5 -->
<script>
Dropzone.options.myDropzone = {
paramName: "file", // The name that will be used to transfer the file
init: function () {
thisDropzone = this;
// 6
$.get('/uploads', function (data) {
if (data == null) {
return;
}
// 7
$.each(data, function (key, value) {
var mockFile = { name: value.name, size: value.size };
thisDropzone.emit("addedfile", mockFile);
thisDropzone.options.thumbnail.call(thisDropzone, mockFile, '/public/uploads/thumbnail_' + value.name);
// thisDropzone.createThumbnailFromUrl(mockFile, '/public/uploads/' + value.name); <- doesn't work...
// Make sure that there is no progress bar, etc...
thisDropzone.emit("complete", mockFile);
});
});
}
};
</script>
</head>
<body>
<!-- 3 -->
<form action="/upload" method="POST" class="dropzone" id="my-dropzone">
<div class="fallback">
<input name="file" type="file" multiple />
<input type="submit" value="Upload" />
</div>
</form>
</body>
</html>

View File

@ -1,21 +1,21 @@
(function() {
var socket = new Ws("ws://localhost:8080/my_endpoint");
socket.OnConnect(function () {
socket.Emit("watch", PAGE_SOURCE);
});
socket.On("watch", function (onlineViews) {
var text = "1 online view";
if (onlineViews > 1) {
text = onlineViews + " online views";
}
document.getElementById("online_views").innerHTML = text;
});
socket.OnDisconnect(function () {
document.getElementById("online_views").innerHTML = "you've been disconnected";
});
})();
(function() {
var socket = new Ws("ws://localhost:8080/my_endpoint");
socket.OnConnect(function () {
socket.Emit("watch", PAGE_SOURCE);
});
socket.On("watch", function (onlineViews) {
var text = "1 online view";
if (onlineViews > 1) {
text = onlineViews + " online views";
}
document.getElementById("online_views").innerHTML = text;
});
socket.OnDisconnect(function () {
document.getElementById("online_views").innerHTML = "you've been disconnected";
});
})();

View File

@ -1,43 +1,43 @@
<html>
<head>
<title>Online visitors example</title>
<style>
body {
margin: 0;
font-family: -apple-system, "San Francisco", "Helvetica Neue", "Noto", "Roboto", "Calibri Light", sans-serif;
color: #212121;
font-size: 1.0em;
line-height: 1.6;
}
.container {
max-width: 750px;
margin: auto;
padding: 15px;
}
#online_views {
font-weight: bold;
font-size: 18px;
}
</style>
</head>
<body>
<div class="container">
<span id="online_views">1 online view</span>
</div>
<script type="text/javascript">
/* take the page source from our passed struct on .Render */
var PAGE_SOURCE = {{ .PageID }}
</script>
<script src="/iris-ws.js"></script>
<script src="/js/visitors.js"></script>
</body>
</html>
<html>
<head>
<title>Online visitors example</title>
<style>
body {
margin: 0;
font-family: -apple-system, "San Francisco", "Helvetica Neue", "Noto", "Roboto", "Calibri Light", sans-serif;
color: #212121;
font-size: 1.0em;
line-height: 1.6;
}
.container {
max-width: 750px;
margin: auto;
padding: 15px;
}
#online_views {
font-weight: bold;
font-size: 18px;
}
</style>
</head>
<body>
<div class="container">
<span id="online_views">1 online view</span>
</div>
<script type="text/javascript">
/* take the page source from our passed struct on .Render */
var PAGE_SOURCE = {{ .PageID }}
</script>
<script src="/iris-ws.js"></script>
<script src="/js/visitors.js"></script>
</body>
</html>

View File

@ -1,29 +1,29 @@
<html>
<head>
<title>Different page, different results</title>
<style>
#online_views {
font-weight: bold;
font-size: 18px;
}
</style>
</head>
<body>
<span id="online_views">1 online view</span>
<script type="text/javascript">
/* take the page source from our passed struct on .Render */
var PAGE_SOURCE = {{ .PageID }}
</script>
<script src="/iris-ws.js"></script>
<script src="/js/visitors.js"></script>
</body>
</html>
<html>
<head>
<title>Different page, different results</title>
<style>
#online_views {
font-weight: bold;
font-size: 18px;
}
</style>
</head>
<body>
<span id="online_views">1 online view</span>
<script type="text/javascript">
/* take the page source from our passed struct on .Render */
var PAGE_SOURCE = {{ .PageID }}
</script>
<script src="/iris-ws.js"></script>
<script src="/js/visitors.js"></script>
</body>
</html>

View File

@ -1,3 +1,3 @@
body{
background-color:silver;
body{
background-color:silver;
}

View File

@ -1,25 +1,25 @@
<html>
<head>
<meta charset="utf-8">
<title>Golang URL Shortener</title>
<link rel="stylesheet" href="/static/css/style.css" />
</head>
<body>
<h2>Golang URL Shortener</h2>
<h3>{{ .FORM_RESULT}}</h3>
<form action="/shorten" method="POST">
<input type="text" name="url" style="width: 35em;" />
<input type="submit" value="Shorten!" />
</form>
{{ if IsPositive .URL_COUNT }}
<p>{{ .URL_COUNT }} URLs shortened</p>
{{ end }}
<form action="/clear_cache" method="POST">
<input type="submit" value="Clear DB" />
</form>
</body>
<html>
<head>
<meta charset="utf-8">
<title>Golang URL Shortener</title>
<link rel="stylesheet" href="/static/css/style.css" />
</head>
<body>
<h2>Golang URL Shortener</h2>
<h3>{{ .FORM_RESULT}}</h3>
<form action="/shorten" method="POST">
<input type="text" name="url" style="width: 35em;" />
<input type="submit" value="Shorten!" />
</form>
{{ if IsPositive .URL_COUNT }}
<p>{{ .URL_COUNT }} URLs shortened</p>
{{ end }}
<form action="/clear_cache" method="POST">
<input type="submit" value="Clear DB" />
</form>
</body>
</html>

View File

@ -1,8 +1,8 @@
<h1>
Title: {{.Title}}
</h1>
<h3>{{.BodyMessage}} </h3>
<hr/>
<h1>
Title: {{.Title}}
</h1>
<h3>{{.BodyMessage}} </h3>
<hr/>
Current time: {{.CurrentTime}}

View File

@ -1,10 +1,10 @@
<html>
<head>
<title>My WebsiteLayout</title>
</head>
<body>
<!-- Render the current template here -->
{{ yield }}
</body>
</html>
<html>
<head>
<title>My WebsiteLayout</title>
</head>
<body>
<!-- Render the current template here -->
{{ yield }}
</body>
</html>

View File

@ -1,12 +1,12 @@
<html>
<head>
<title>Layout</title>
</head>
<body>
<h1>This is the global layout</h1>
<br />
<!-- Render the current template here -->
{{ yield }}
</body>
</html>
<html>
<head>
<title>Layout</title>
</head>
<body>
<h1>This is the global layout</h1>
<br />
<!-- Render the current template here -->
{{ yield }}
</body>
</html>

View File

@ -1,12 +1,12 @@
<html>
<head>
<title>my Layout</title>
</head>
<body>
<h1>This is the layout for the /my/ and /my/other routes only</h1>
<br />
<!-- Render the current template here -->
{{ yield }}
</body>
</html>
<html>
<head>
<title>my Layout</title>
</head>
<body>
<h1>This is the layout for the /my/ and /my/other routes only</h1>
<br />
<!-- Render the current template here -->
{{ yield }}
</body>
</html>

View File

@ -1,7 +1,7 @@
<div style="background-color: black; color: blue">
<h1>Page 1 {{ greet "iris developer"}}</h1>
{{ render "partials/page1_partial1.html"}}
</div>
<div style="background-color: black; color: blue">
<h1>Page 1 {{ greet "iris developer"}}</h1>
{{ render "partials/page1_partial1.html"}}
</div>

View File

@ -1,3 +1,3 @@
<div style="background-color: white; color: red">
<h1>Page 1's Partial 1</h1>
</div>
<div style="background-color: white; color: red">
<h1>Page 1's Partial 1</h1>
</div>

View File

@ -1,11 +1,11 @@
<html>
<head>
<title>Hi iris</title>
</head>
<body>
<h1>Hi {{.Name}} </h1>
</body>
<html>
<head>
<title>Hi iris</title>
</head>
<body>
<h1>Hi {{.Name}} </h1>
</body>
</html>

View File

@ -1,8 +1,8 @@
<html>
<head>
<title>{{.Title}}</title>
</head>
<body>
<h1>Hi {{.Name}} </h1>
</body>
</html>
<html>
<head>
<title>{{.Title}}</title>
</head>
<body>
<h1>Hi {{.Name}} </h1>
</body>
</html>

View File

@ -1,11 +1,11 @@
<html>
<head>
<title>My Layout</title>
</head>
<body>
<h1>[layout] Body content is below...</h1>
<!-- Render the current template here -->
{{ yield }}
</body>
</html>
<html>
<head>
<title>My Layout</title>
</head>
<body>
<h1>[layout] Body content is below...</h1>
<!-- Render the current template here -->
{{ yield }}
</body>
</html>

View File

@ -1,4 +1,4 @@
<h1>
Title: {{.Title}}
</h1>
<h1>
Title: {{.Title}}
</h1>
<h3>Message: {{.Message}} </h3>

View File

@ -1,3 +1,3 @@
## Info
This folder examines the {{render "dir/templatefilename"}} functionality to manually render any template inside any template
## Info
This folder examines the {{render "dir/templatefilename"}} functionality to manually render any template inside any template

View File

@ -1,12 +1,12 @@
<html>
<head>
<title>Layout</title>
</head>
<body>
<h1>This is the global layout</h1>
<br />
<!-- Render the current template here -->
{{ yield }}
</body>
</html>
<html>
<head>
<title>Layout</title>
</head>
<body>
<h1>This is the global layout</h1>
<br />
<!-- Render the current template here -->
{{ yield }}
</body>
</html>

View File

@ -1,12 +1,12 @@
<html>
<head>
<title>my Layout</title>
</head>
<body>
<h1>This is the layout for the /my/ and /my/other routes only</h1>
<br />
<!-- Render the current template here -->
{{ yield }}
</body>
</html>
<html>
<head>
<title>my Layout</title>
</head>
<body>
<h1>This is the layout for the /my/ and /my/other routes only</h1>
<br />
<!-- Render the current template here -->
{{ yield }}
</body>
</html>

View File

@ -1,7 +1,7 @@
<div style="background-color: black; color: blue">
<h1>Page 1 {{ greet "iris developer"}}</h1>
{{ render "partials/page1_partial1.html"}}
</div>
<div style="background-color: black; color: blue">
<h1>Page 1 {{ greet "iris developer"}}</h1>
{{ render "partials/page1_partial1.html"}}
</div>

View File

@ -1,3 +1,3 @@
<div style="background-color: white; color: red">
<h1>Page 1's Partial 1</h1>
</div>
<div style="background-color: white; color: red">
<h1>Page 1's Partial 1</h1>
</div>

View File

@ -1,25 +1,25 @@
<a href="{{urlpath "my-page1"}}">/mypath</a>
<br />
<br />
<a href="{{urlpath "my-page2" "theParam1" "theParam2"}}">/mypath2/{paramfirst}/{paramsecond}</a>
<br />
<br />
<a href="{{urlpath "my-page3" "theParam1" "theParam2AfterStatic"}}">/mypath3/{paramfirst}/statichere/{paramsecond}</a>
<br />
<br />
<a href="{{urlpath "my-page4" "theParam1" "theparam2AfterStatic" "otherParam" "matchAnything"}}">
/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}</a>
<br />
<br />
<a href="{{urlpath "my-page5" "theParam1" "theParam2Afterstatichere" "otherParam" "matchAnythingAfterStatic"}}">
/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{anything:path}</a>
<br />
<br />
<a href={{urlpath "my-page6" .ParamsAsArray }}>
/mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}
</a>
<a href="{{urlpath "my-page1"}}">/mypath</a>
<br />
<br />
<a href="{{urlpath "my-page2" "theParam1" "theParam2"}}">/mypath2/{paramfirst}/{paramsecond}</a>
<br />
<br />
<a href="{{urlpath "my-page3" "theParam1" "theParam2AfterStatic"}}">/mypath3/{paramfirst}/statichere/{paramsecond}</a>
<br />
<br />
<a href="{{urlpath "my-page4" "theParam1" "theparam2AfterStatic" "otherParam" "matchAnything"}}">
/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}</a>
<br />
<br />
<a href="{{urlpath "my-page5" "theParam1" "theParam2Afterstatichere" "otherParam" "matchAnythingAfterStatic"}}">
/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{anything:path}</a>
<br />
<br />
<a href={{urlpath "my-page6" .ParamsAsArray }}>
/mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}
</a>

View File

@ -1,32 +1,32 @@
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know
127.0.0.1 username1.127.0.0.1
127.0.0.1 username2.127.0.0.1
127.0.0.1 username3.127.0.0.1
127.0.0.1 username4.127.0.0.1
127.0.0.1 username5.127.0.0.1
# note that you can always use custom subdomains
#-END iris-
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know
127.0.0.1 username1.127.0.0.1
127.0.0.1 username2.127.0.0.1
127.0.0.1 username3.127.0.0.1
127.0.0.1 username4.127.0.0.1
127.0.0.1 username5.127.0.0.1
# note that you can always use custom subdomains
#-END iris-
# Windows: Drive:/Windows/system32/drivers/etc/hosts, on Linux: /etc/hosts

View File

@ -1,29 +1,29 @@
<!-- the only difference between normal named routes and dynamic subdomains named routes is that the first argument of url
is the subdomain part instead of named parameter-->
<a href="{{url "my-page1" "username1"}}">username1.127.0.0.1:8080/mypath</a>
<br />
<br />
<a href="{{url "my-page2" "username2" "theParam1" "theParam2"}}">
username2.127.0.0.1:8080/mypath2/{paramfirst}/{paramsecond}
</a>
<br />
<br />
<a href="{{url "my-page3" "username3" "theParam1" "theParam2AfterStatic"}}">
username3.127.0.0.1:8080/mypath3/{paramfirst}/statichere/{paramsecond}
</a>
<br />
<br />
<a href="{{url "my-page4" "username4" "theParam1" "theparam2AfterStatic" "otherParam" "matchAnything"}}">
username4.127.0.0.1:8080/mypath4/{paramfirst}/statichere/{paramsecond}/{otherParam}/{something:path}
</a>
<br />
<br />
<a href="{{url "my-page5" "username5" "theParam1" "theparam2AfterStatic" "otherParam" "matchAnything"}}">
username5.127.0.0.1:8080/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{something:path}
</a>
<br/>
<br/>
<a href="{{url "my-page6" .ParamsAsArray }}">
username5.127.0.0.1:8080/mypath6/{paramfirst}/{paramsecond}/staticParam/{paramThirdAfterStatic}
</a>
<!-- the only difference between normal named routes and dynamic subdomains named routes is that the first argument of url
is the subdomain part instead of named parameter-->
<a href="{{url "my-page1" "username1"}}">username1.127.0.0.1:8080/mypath</a>
<br />
<br />
<a href="{{url "my-page2" "username2" "theParam1" "theParam2"}}">
username2.127.0.0.1:8080/mypath2/{paramfirst}/{paramsecond}
</a>
<br />
<br />
<a href="{{url "my-page3" "username3" "theParam1" "theParam2AfterStatic"}}">
username3.127.0.0.1:8080/mypath3/{paramfirst}/statichere/{paramsecond}
</a>
<br />
<br />
<a href="{{url "my-page4" "username4" "theParam1" "theparam2AfterStatic" "otherParam" "matchAnything"}}">
username4.127.0.0.1:8080/mypath4/{paramfirst}/statichere/{paramsecond}/{otherParam}/{something:path}
</a>
<br />
<br />
<a href="{{url "my-page5" "username5" "theParam1" "theparam2AfterStatic" "otherParam" "matchAnything"}}">
username5.127.0.0.1:8080/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{something:path}
</a>
<br/>
<br/>
<a href="{{url "my-page6" .ParamsAsArray }}">
username5.127.0.0.1:8080/mypath6/{paramfirst}/{paramsecond}/staticParam/{paramThirdAfterStatic}
</a>

View File

@ -1,39 +1,39 @@
<input id="input" type="text" />
<button onclick="send()">Send</button>
<pre id="output"></pre>
<script src="/iris-ws.js"></script>
<script>
var scheme = document.location.protocol == "https:" ? "wss" : "ws";
var port = document.location.port ? (":" + document.location.port) : "";
// see app.Get("/echo", ws.Handler()) on main.go
var wsURL = scheme + "://" + document.location.hostname + port+"/echo";
var input = document.getElementById("input");
var output = document.getElementById("output");
// Ws comes from the auto-served '/iris-ws.js'
var socket = new Ws(wsURL)
socket.OnConnect(function () {
output.innerHTML += "Status: Connected\n";
});
socket.OnDisconnect(function () {
output.innerHTML += "Status: Disconnected\n";
});
// read events from the server
socket.On("chat", function (msg) {
addMessage(msg)
});
function send() {
addMessage("Me: " + input.value) // write ourselves
socket.Emit("chat", input.value);// send chat event data to the websocket server
input.value = ""; // clear the input
}
function addMessage(msg) {
output.innerHTML += msg + "\n";
}
<input id="input" type="text" />
<button onclick="send()">Send</button>
<pre id="output"></pre>
<script src="/iris-ws.js"></script>
<script>
var scheme = document.location.protocol == "https:" ? "wss" : "ws";
var port = document.location.port ? (":" + document.location.port) : "";
// see app.Get("/echo", ws.Handler()) on main.go
var wsURL = scheme + "://" + document.location.hostname + port+"/echo";
var input = document.getElementById("input");
var output = document.getElementById("output");
// Ws comes from the auto-served '/iris-ws.js'
var socket = new Ws(wsURL)
socket.OnConnect(function () {
output.innerHTML += "Status: Connected\n";
});
socket.OnDisconnect(function () {
output.innerHTML += "Status: Disconnected\n";
});
// read events from the server
socket.On("chat", function (msg) {
addMessage(msg)
});
function send() {
addMessage("Me: " + input.value) // write ourselves
socket.Emit("chat", input.value);// send chat event data to the websocket server
input.value = ""; // clear the input
}
function addMessage(msg) {
output.innerHTML += msg + "\n";
}
</script>

View File

@ -1,38 +1,38 @@
var messageTxt;
var messages;
$(function () {
messageTxt = $("#messageTxt");
messages = $("#messages");
w = new Ws("ws://" + HOST + "/my_endpoint");
w.OnConnect(function () {
console.log("Websocket connection established");
});
w.OnDisconnect(function () {
appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
});
w.On("chat", function (message) {
appendMessage($("<div>" + message + "</div>"));
});
$("#sendBtn").click(function () {
w.Emit("chat", messageTxt.val().toString());
messageTxt.val("");
});
})
function appendMessage(messageDiv) {
var theDiv = messages[0];
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
messageDiv.appendTo(messages);
if (doScroll) {
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
}
}
var messageTxt;
var messages;
$(function () {
messageTxt = $("#messageTxt");
messages = $("#messages");
w = new Ws("ws://" + HOST + "/my_endpoint");
w.OnConnect(function () {
console.log("Websocket connection established");
});
w.OnDisconnect(function () {
appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
});
w.On("chat", function (message) {
appendMessage($("<div>" + message + "</div>"));
});
$("#sendBtn").click(function () {
w.Emit("chat", messageTxt.val().toString());
messageTxt.val("");
});
})
function appendMessage(messageDiv) {
var theDiv = messages[0];
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
messageDiv.appendTo(messages);
if (doScroll) {
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
}
}

View File

@ -1,24 +1,24 @@
<html>
<head>
<title>{{ .Title}}</title>
</head>
<body>
<div id="messages"
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
</div>
<input type="text" id="messageTxt" />
<button type="button" id="sendBtn">Send</button>
<script type="text/javascript">
var HOST = {{.Host}}
</script>
<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
<!-- This is auto-serving by the iris, you don't need to have this file in your disk-->
<script src="/iris-ws.js" type="text/javascript"></script>
<!-- -->
<script src="js/chat.js" type="text/javascript"></script>
</body>
</html>
<html>
<head>
<title>{{ .Title}}</title>
</head>
<body>
<div id="messages"
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
</div>
<input type="text" id="messageTxt" />
<button type="button" id="sendBtn">Send</button>
<script type="text/javascript">
var HOST = {{.Host}}
</script>
<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
<!-- This is auto-serving by the iris, you don't need to have this file in your disk-->
<script src="/iris-ws.js" type="text/javascript"></script>
<!-- -->
<script src="js/chat.js" type="text/javascript"></script>
</body>
</html>

View File

@ -1,38 +1,38 @@
var messageTxt;
var messages;
$(function () {
messageTxt = $("#messageTxt");
messages = $("#messages");
w = new WebSocket("ws://" + HOST + "/my_endpoint");
w.onopen = function () {
console.log("Websocket connection enstablished");
};
w.onclose = function () {
appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
};
w.onmessage = function(message){
appendMessage($("<div>" + message.data + "</div>"));
};
$("#sendBtn").click(function () {
w.send(messageTxt.val().toString());
messageTxt.val("");
});
})
function appendMessage(messageDiv) {
var theDiv = messages[0];
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
messageDiv.appendTo(messages);
if (doScroll) {
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
}
}
var messageTxt;
var messages;
$(function () {
messageTxt = $("#messageTxt");
messages = $("#messages");
w = new WebSocket("ws://" + HOST + "/my_endpoint");
w.onopen = function () {
console.log("Websocket connection enstablished");
};
w.onclose = function () {
appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
};
w.onmessage = function(message){
appendMessage($("<div>" + message.data + "</div>"));
};
$("#sendBtn").click(function () {
w.send(messageTxt.val().toString());
messageTxt.val("");
});
})
function appendMessage(messageDiv) {
var theDiv = messages[0];
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
messageDiv.appendTo(messages);
if (doScroll) {
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
}
}

View File

@ -1,21 +1,21 @@
<html>
<head>
<title>{{ .Title}}</title>
</head>
<body>
<div id="messages"
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
</div>
<input type="text" id="messageTxt" />
<button type="button" id="sendBtn">Send</button>
<script type="text/javascript">
var HOST = {{.Host}}
</script>
<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
<script src="js/chat.js" type="text/javascript"></script>
</body>
</html>
<html>
<head>
<title>{{ .Title}}</title>
</head>
<body>
<div id="messages"
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
</div>
<input type="text" id="messageTxt" />
<button type="button" id="sendBtn">Send</button>
<script type="text/javascript">
var HOST = {{.Host}}
</script>
<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
<script src="js/chat.js" type="text/javascript"></script>
</body>
</html>

View File

@ -1,38 +1,38 @@
var messageTxt;
var messages;
$(function () {
messageTxt = $("#messageTxt");
messages = $("#messages");
/* secure wss because we ListenTLS */
w = new Ws("wss://" + HOST + "/my_endpoint");
w.OnConnect(function () {
console.log("Websocket connection established");
});
w.OnDisconnect(function () {
appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
});
w.On("chat", function (message) {
appendMessage($("<div>" + message + "</div>"));
});
$("#sendBtn").click(function () {
w.Emit("chat", messageTxt.val().toString());
messageTxt.val("");
});
})
function appendMessage(messageDiv) {
var theDiv = messages[0];
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
messageDiv.appendTo(messages);
if (doScroll) {
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
}
}
var messageTxt;
var messages;
$(function () {
messageTxt = $("#messageTxt");
messages = $("#messages");
/* secure wss because we ListenTLS */
w = new Ws("wss://" + HOST + "/my_endpoint");
w.OnConnect(function () {
console.log("Websocket connection established");
});
w.OnDisconnect(function () {
appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
});
w.On("chat", function (message) {
appendMessage($("<div>" + message + "</div>"));
});
$("#sendBtn").click(function () {
w.Emit("chat", messageTxt.val().toString());
messageTxt.val("");
});
})
function appendMessage(messageDiv) {
var theDiv = messages[0];
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
messageDiv.appendTo(messages);
if (doScroll) {
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
}
}

View File

@ -1,24 +1,24 @@
<html>
<head>
<title>{{ .Title}}</title>
</head>
<body>
<div id="messages"
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
</div>
<input type="text" id="messageTxt" />
<button type="button" id="sendBtn">Send</button>
<script type="text/javascript">
var HOST = {{.Host}}
</script>
<script src="/js/vendor/jquery-2.2.3.min.js"></script>
<!-- This is auto-serving by the iris, you don't need to have this file in your disk-->
<script src="/iris-ws.js"></script>
<!-- -->
<script src="/js/chat.js"></script>
</body>
</html>
<html>
<head>
<title>{{ .Title}}</title>
</head>
<body>
<div id="messages"
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
</div>
<input type="text" id="messageTxt" />
<button type="button" id="sendBtn">Send</button>
<script type="text/javascript">
var HOST = {{.Host}}
</script>
<script src="/js/vendor/jquery-2.2.3.min.js"></script>
<!-- This is auto-serving by the iris, you don't need to have this file in your disk-->
<script src="/iris-ws.js"></script>
<!-- -->
<script src="/js/chat.js"></script>
</body>
</html>

View File

@ -1,38 +1,38 @@
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="/socket.io-1.3.7.js"></script>
<script src="/jquery-1.11.1.js"></script>
<script>
var socket = io();
$('form').submit(function(){
socket.emit('chat message with ack', $('#m').val(), function(data){
$('#messages').append($('<li>').text('ACK CALLBACK: ' + data));
});
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
</script>
</body>
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="/socket.io-1.3.7.js"></script>
<script src="/jquery-1.11.1.js"></script>
<script>
var socket = io();
$('form').submit(function(){
socket.emit('chat message with ack', $('#m').val(), function(data){
$('#messages').append($('<li>').text('ACK CALLBACK: ' + data));
});
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long