mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
Update to version 8.5.5
Former-commit-id: b5be58709f17758a8df3ebc99270b97ccd8b18f2
This commit is contained in:
parent
6607008054
commit
666bcacf20
|
@ -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!
|
||||
|
|
|
@ -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)
|
|
@ -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}}
|
|
@ -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>
|
2
_examples/cache/simple/main.go
vendored
2
_examples/cache/simple/main.go
vendored
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
|
@ -1,3 +1,3 @@
|
|||
body {
|
||||
background-color: black;
|
||||
}
|
||||
body {
|
||||
background-color: black;
|
||||
}
|
||||
|
|
18380
_examples/file-server/basic/assets/js/jquery-2.1.1.js
vendored
18380
_examples/file-server/basic/assets/js/jquery-2.1.1.js
vendored
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
|
@ -1,3 +1,3 @@
|
|||
body {
|
||||
background-color: black;
|
||||
}
|
||||
body {
|
||||
background-color: black;
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -1,3 +1,3 @@
|
|||
body {
|
||||
background-color: black;
|
||||
}
|
||||
body {
|
||||
background-color: black;
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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.
|
|
@ -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-----
|
||||
|
|
|
@ -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-----
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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%;
|
||||
}
|
||||
}
|
|
@ -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.
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,3 +1,3 @@
|
|||
<p>
|
||||
Welcome back <strong>{{.User.Firstname}}</strong>!
|
||||
<p>
|
||||
Welcome back <strong>{{.User.Firstname}}</strong>!
|
||||
</p>
|
|
@ -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>
|
|
@ -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.
|
|
@ -1,3 +1,3 @@
|
|||
# Repositories
|
||||
|
||||
# Repositories
|
||||
|
||||
The package which has direct access to the "datasource" and can manipulate data directly.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
body {
|
||||
background-color: black;
|
||||
}
|
||||
body {
|
||||
background-color: black;
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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>© 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>© 2017 - {{.AppOwner}}</p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -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%;
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,3 +1,3 @@
|
|||
<p>
|
||||
Welcome back <strong>{{.User.Firstname}}</strong>!
|
||||
<p>
|
||||
Welcome back <strong>{{.User.Firstname}}</strong>!
|
||||
</p>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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-
|
||||
|
|
|
@ -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-
|
||||
|
|
|
@ -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-
|
||||
|
|
|
@ -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-
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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.
|
|
@ -1,3 +1,3 @@
|
|||
<div>
|
||||
{{.Message}}
|
||||
<div>
|
||||
{{.Message}}
|
||||
</div>
|
|
@ -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>
|
|
@ -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)
|
|
@ -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.
|
|
@ -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>
|
|
@ -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";
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
body{
|
||||
background-color:silver;
|
||||
body{
|
||||
background-color:silver;
|
||||
}
|
|
@ -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>
|
|
@ -1,8 +1,8 @@
|
|||
<h1>
|
||||
Title: {{.Title}}
|
||||
</h1>
|
||||
<h3>{{.BodyMessage}} </h3>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h1>
|
||||
Title: {{.Title}}
|
||||
</h1>
|
||||
<h3>{{.BodyMessage}} </h3>
|
||||
|
||||
<hr/>
|
||||
|
||||
Current time: {{.CurrentTime}}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<h1>
|
||||
Title: {{.Title}}
|
||||
</h1>
|
||||
<h1>
|
||||
Title: {{.Title}}
|
||||
</h1>
|
||||
<h3>Message: {{.Message}} </h3>
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue
Block a user