1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-11 05:55:57 +00:00

Add some _examples in the main repository too.

Former-commit-id: 98895c34115ec2076b431332f0ffe9645adf7590
This commit is contained in:
Gerasimos (Makis) Maropoulos
2017-03-13 15:16:12 +02:00
parent d76d9b1ec6
commit f487cd0029
29 changed files with 1318 additions and 248 deletions

33
_examples/README.md Normal file
View File

@@ -0,0 +1,33 @@
## Examples
This folder provides easy to understand code snippets on how to get started with web development with the Go programming language using the [Iris](https://github.com/kataras/iris) web framework.
It doesn't contains "best ways" neither explains all its features. It's just a simple, practical cookbook for young Go developers!
Developers should read the official [documentation](https://godoc.org/gopkg.in/kataras/iris.v6) in depth.
<a href ="https://github.com/kataras/iris"> <img src="http://iris-go.com/assets/book/cover_4.jpg" width="300" /> </a>
## Table of Contents
* [Hello World](examples/hello-world/main.go)
* [Routes (using httprouter)](examples/routes-using-httprouter/main.go)
* [Routes (using gorillamux)](examples/routes-using-gorillamux/main.go)
* [Templates](examples/templates/main.go)
* [Forms](examples/forms/main.go)
* [JSON](examples/json/main.go)
* [Upload Files](examples/upload-files/main.go)
* [Static Files](examples/static-files/main.go)
* [Favicon](examples/favicon/main.go)
* [Password Hashing](examples/password-hashing/main.go)
* [Sessions](examples/sessions/main.go)
* [Websockets](examples/websockets/main.go)
* [Markdown and Cache](examples/cache-markdown/main.go)
* [Online Visitors](examples/online-visitors/main.go)
* [URL Shortener](examples/url-shortener/main.go)
> Take look at the [community examples](https://github.com/iris-contrib/examples) too!

View File

@@ -0,0 +1,82 @@
package main
import (
"time"
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
)
var testMarkdownContents = `## Hello Markdown
This is a sample of Markdown contents
Features
--------
All features of Sundown are supported, including:
* **Compatibility**. The Markdown v1.0.3 test suite passes with
the --tidy option. Without --tidy, the differences are
mostly in whitespace and entity escaping, where blackfriday is
more consistent and cleaner.
* **Common extensions**, including table support, fenced code
blocks, autolinks, strikethroughs, non-strict emphasis, etc.
* **Safety**. Blackfriday is paranoid when parsing, making it safe
to feed untrusted user input without fear of bad things
happening. The test suite stress tests this and there are no
known inputs that make it crash. If you find one, please let me
know and send me the input that does it.
NOTE: "safety" in this context means *runtime safety only*. In order to
protect yourself against JavaScript injection in untrusted content, see
[this example](https://github.com/russross/blackfriday#sanitize-untrusted-content).
* **Fast processing**. It is fast enough to render on-demand in
most web applications without having to cache the output.
* **Thread safety**. You can run multiple parsers in different
goroutines without ill effect. There is no dependence on global
shared state.
* **Minimal dependencies**. Blackfriday only depends on standard
library packages in Go. The source code is pretty
self-contained, so it is easy to add to any project, including
Google App Engine projects.
* **Standards compliant**. Output successfully validates using the
W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional.
[this is a link](https://github.com/kataras/iris) `
func main() {
app := iris.New()
// output startup banner and error logs on os.Stdout
app.Adapt(iris.DevLogger())
// set the router, you can choose gorillamux too
app.Adapt(httprouter.New())
app.Get("/hi", app.Cache(func(c *iris.Context) {
c.WriteString("Hi this is a big content, do not try cache on small content it will not make any significant difference!")
}, time.Duration(10)*time.Second))
bodyHandler := func(ctx *iris.Context) {
ctx.Markdown(iris.StatusOK, testMarkdownContents)
}
expiration := time.Duration(5 * time.Second)
app.Get("/", app.Cache(bodyHandler, expiration))
// if expiration is <=time.Second then the cache tries to set the expiration from the "cache-control" maxage header's value(in seconds)
// // if this header doesn't founds then the default is 5 minutes
app.Get("/cache_control", app.Cache(func(ctx *iris.Context) {
ctx.HTML(iris.StatusOK, "<h1>Hello!</h1>")
}, -1))
app.Listen(":8080")
}

View File

@@ -0,0 +1,23 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
)
func main() {
app := iris.New()
app.Adapt(httprouter.New())
// This will serve the ./static/favicons/iris_favicon_32_32.ico to: localhost:8080/favicon.ico
app.Favicon("./static/favicons/iris_favicon_32_32.ico")
// app.Favicon("./static/favicons/iris_favicon_32_32.ico", "/favicon_32_32.ico")
// This will serve the ./static/favicons/iris_favicon_32_32.ico to: localhost:8080/favicon_32_32.ico
app.Get("/", func(ctx *iris.Context) {
ctx.HTML(iris.StatusOK, `You should see the favicon now at the side of your browser,
if not, please refresh or clear the browser's cache.`)
})
app.Listen(":8080")
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,47 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
)
// ContactDetails the information from user
type ContactDetails struct {
Email string `form:"email"`
Subject string `form:"subject"`
Message string `form:"message"`
}
func main() {
app := iris.New()
app.Adapt(httprouter.New())
// Parse all files inside `./mytemplates` directory ending with `.html`
app.Adapt(view.HTML("./mytemplates", ".html"))
app.Get("/", func(ctx *iris.Context) {
ctx.Render("forms.html", nil)
})
// Equivalent with app.HandleFunc("POST", ...)
app.Post("/", func(ctx *iris.Context) {
// details := ContactDetails{
// Email: ctx.FormValue("email"),
// Subject: ctx.FormValue("subject"),
// Message: ctx.FormValue("message"),
// }
// or simply:
var details ContactDetails
ctx.ReadForm(&details)
// do something with details
_ = details
ctx.Render("forms.html", struct{ Success bool }{true})
})
app.Listen(":8080")
}

View File

@@ -0,0 +1,14 @@
{{if .Success}}
<h1>Thanks for your message!</h1>
{{else}}
<h1>Contact</h1>
<form method="POST">
<label>Email:</label><br />
<input type="text" name="email"><br />
<label>Subject:</label><br />
<input type="text" name="subject"><br />
<label>Message:</label><br />
<textarea name="message"></textarea><br />
<input type="submit">
</form>
{{end}}

View File

@@ -0,0 +1,20 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
)
func main() {
app := iris.New()
// Adapt the "httprouter", faster,
// but it has limits on named path parameters' validation,
// you can adapt "gorillamux" if you need regexp path validation!
app.Adapt(httprouter.New())
app.HandleFunc("GET", "/", func(ctx *iris.Context) {
ctx.Writef("hello world\n")
})
app.Listen(":8080")
}

View File

@@ -0,0 +1,37 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
)
// User bind struct
type User struct {
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
Age int `json:"age"`
}
func main() {
app := iris.New()
app.Adapt(httprouter.New())
app.Post("/decode", func(ctx *iris.Context) {
var user User
ctx.ReadJSON(&user)
ctx.Writef("%s %s is %d years old!", user.Firstname, user.Lastname, user.Age)
})
app.Get("/encode", func(ctx *iris.Context) {
peter := User{
Firstname: "John",
Lastname: "Doe",
Age: 25,
}
ctx.JSON(iris.StatusOK, peter)
})
app.Listen(":8080")
}

View File

@@ -0,0 +1,170 @@
package main
import (
"sync/atomic"
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
"gopkg.in/kataras/iris.v6/adaptors/websocket"
)
var (
app *iris.Framework
ws websocket.Server
)
func init() {
// init the server instance
app = iris.New()
// adapt a logger in dev mode
app.Adapt(iris.DevLogger())
// adapt router
app.Adapt(httprouter.New())
// adapt templaes
app.Adapt(view.HTML("./templates", ".html").Reload(true))
// adapt websocket
ws = websocket.New(websocket.Config{Endpoint: "/my_endpoint"})
ws.OnConnection(HandleWebsocketConnection)
app.Adapt(ws)
}
type page struct {
PageID string
}
func main() {
app.StaticWeb("/js", "./static/assets/js")
h := func(ctx *iris.Context) {
ctx.Render("index.html", page{PageID: "index page"})
}
h2 := func(ctx *iris.Context) {
ctx.Render("other.html", page{PageID: "other page"})
}
// Open some browser tabs/or windows
// and navigate to
// http://localhost:8080/ and http://localhost:8080/other
// Each page has its own online-visitors counter.
app.Get("/", h)
app.Get("/other", h2)
app.Listen(":8080")
}
type pageView struct {
source string
count uint64
}
func (v *pageView) increment() {
atomic.AddUint64(&v.count, 1)
}
func (v *pageView) decrement() {
oldCount := v.count
if oldCount > 0 {
atomic.StoreUint64(&v.count, oldCount-1)
}
}
func (v *pageView) getCount() uint64 {
val := atomic.LoadUint64(&v.count)
return val
}
type (
pageViews []pageView
)
func (v *pageViews) Add(source string) {
args := *v
n := len(args)
for i := 0; i < n; i++ {
kv := &args[i]
if kv.source == source {
kv.increment()
return
}
}
c := cap(args)
if c > n {
args = args[:n+1]
kv := &args[n]
kv.source = source
kv.count = 1
*v = args
return
}
kv := pageView{}
kv.source = source
kv.count = 1
*v = append(args, kv)
}
func (v *pageViews) Get(source string) *pageView {
args := *v
n := len(args)
for i := 0; i < n; i++ {
kv := &args[i]
if kv.source == source {
return kv
}
}
return nil
}
func (v *pageViews) Reset() {
*v = (*v)[:0]
}
var v pageViews
// HandleWebsocketConnection handles the online viewers per example(gist source)
func HandleWebsocketConnection(c websocket.Connection) {
c.On("watch", func(pageSource string) {
v.Add(pageSource)
// join the socket to a room linked with the page source
c.Join(pageSource)
viewsCount := v.Get(pageSource).getCount()
if viewsCount == 0 {
viewsCount++ // count should be always > 0 here
}
c.To(pageSource).Emit("watch", viewsCount)
})
c.OnLeave(func(roomName string) {
if roomName != c.ID() { // if the roomName it's not the connection iself
// the roomName here is the source, this is the only room(except the connection's ID room) which we join the users to.
pageV := v.Get(roomName)
if pageV == nil {
return // for any case that this room is not a pageView source
}
// decrement -1 the specific counter for this page source.
pageV.decrement()
// 1. open 30 tabs.
// 2. close the browser.
// 3. re-open the browser
// 4. should be v.getCount() = 1
// in order to achieve the previous flow we should decrement exactly when the user disconnects
// but emit the result a little after, on a goroutine
// getting all connections within this room and emit the online views one by one.
// note:
// we can also add a time.Sleep(2-3 seconds) inside the goroutine at the future if we don't need 'real-time' updates.
go func(currentConnID string) {
for _, conn := range ws.GetConnectionsByRoom(roomName) {
if conn.ID() != currentConnID {
conn.Emit("watch", pageV.getCount())
}
}
}(c.ID())
}
})
}

View File

@@ -0,0 +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";
});
})();

View File

@@ -0,0 +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>

View File

@@ -0,0 +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>

View File

@@ -0,0 +1,28 @@
package main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
return string(bytes), err
}
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
func main() {
password := "secret"
hash, _ := HashPassword(password) // ignore error for the sake of simplicity
fmt.Println("Password:", password)
fmt.Println("Hash: ", hash)
match := CheckPasswordHash(password, hash)
fmt.Println("Match: ", match)
}

View File

@@ -0,0 +1,28 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/gorillamux"
)
func main() {
app := iris.New()
// Adapt the "httprouter", you can use "gorillamux" too.
app.Adapt(gorillamux.New())
userAges := map[string]int{
"Alice": 25,
"Bob": 30,
"Claire": 29,
}
// Equivalent with app.HandleFunc("GET", ...)
app.Get("/users/{name}", func(ctx *iris.Context) {
name := ctx.Param("name")
age := userAges[name]
ctx.Writef("%s is %d years old!", name, age)
})
app.Listen(":8080")
}

View File

@@ -0,0 +1,27 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
)
func main() {
app := iris.New()
app.Adapt(httprouter.New())
userAges := map[string]int{
"Alice": 25,
"Bob": 30,
"Claire": 29,
}
// Equivalent with app.HandleFunc("GET", ...)
app.Get("/users/:name", func(ctx *iris.Context) {
name := ctx.Param("name")
age := userAges[name]
ctx.Writef("%s is %d years old!", name, age)
})
app.Listen(":8080")
}

View File

@@ -0,0 +1,54 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/sessions"
)
var (
key = "my_sessionid"
)
func secret(ctx *iris.Context) {
// Check if user is authenticated
if auth, _ := ctx.Session().GetBoolean("authenticated"); !auth {
ctx.EmitError(iris.StatusForbidden)
return
}
// Print secret message
ctx.WriteString("The cake is a lie!")
}
func login(ctx *iris.Context) {
session := ctx.Session()
// Authentication goes here
// ...
// Set user as authenticated
session.Set("authenticated", true)
}
func logout(ctx *iris.Context) {
session := ctx.Session()
// Revoke users authentication
session.Set("authenticated", false)
}
func main() {
app := iris.New()
app.Adapt(httprouter.New())
sess := sessions.New(sessions.Config{Cookie: key})
app.Adapt(sess)
app.Get("/secret", secret)
app.Get("/login", login)
app.Get("/logout", logout)
app.Listen(":8080")
}

View File

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

View File

@@ -0,0 +1,16 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
)
func main() {
app := iris.New()
app.Adapt(httprouter.New())
// first parameter is the request path
// second is the operating system directory
app.StaticWeb("/static", "./assets")
app.Listen(":8080")
}

View File

@@ -0,0 +1,39 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
)
// Todo bind struct
type Todo struct {
Task string
Done bool
}
func main() {
// Configuration is optional
app := iris.New(iris.Configuration{Gzip: false, Charset: "UTF-8"})
// Adapt a logger which will print all errors to os.Stdout
app.Adapt(iris.DevLogger())
// Adapt the httprouter (we will use that on all examples)
app.Adapt(httprouter.New())
// Parse all files inside `./mytemplates` directory ending with `.html`
app.Adapt(view.HTML("./mytemplates", ".html"))
todos := []Todo{
{"Learn Go", true},
{"Read GopherBOOk", true},
{"Create a web app in Go", false},
}
app.Get("/", func(ctx *iris.Context) {
ctx.Render("todos.html", struct{ Todos []Todo }{todos})
})
app.Listen(":8080")
}

View File

@@ -0,0 +1,10 @@
<h1>Todos</h1>
<ul>
{{range .Todos}}
{{if .Done}}
<li><s>{{.Task}}</s></li>
{{else}}
<li>{{.Task}}</li>
{{end}}
{{end}}
</ul>

View File

@@ -0,0 +1,69 @@
package main
import (
"crypto/md5"
"fmt"
"io"
"os"
"strconv"
"time"
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
)
func main() {
app := iris.New()
app.Adapt(iris.DevLogger())
app.Adapt(httprouter.New())
app.Adapt(view.HTML("./templates", ".html"))
// Serve the form.html to the user
app.Get("/upload", func(ctx *iris.Context) {
//create a token (optionally)
now := time.Now().Unix()
h := md5.New()
io.WriteString(h, strconv.FormatInt(now, 10))
token := fmt.Sprintf("%x", h.Sum(nil))
//render the form with the token for any use you like
ctx.Render("upload_form.html", token)
})
// Handle the post request from the upload_form.html to the server
app.Post("/upload", iris.LimitRequestBodySize(10<<20),
func(ctx *iris.Context) {
// or use ctx.SetMaxRequestBodySize(10 << 20)
//to limit the uploaded file(s) size.
// Get the file from the request
file, info, err := ctx.FormFile("uploadfile")
if err != nil {
ctx.HTML(iris.StatusInternalServerError,
"Error while uploading: <b>"+err.Error()+"</b>")
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("./uploads/"+fname,
os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
ctx.HTML(iris.StatusInternalServerError,
"Error while uploading: <b>"+err.Error()+"</b>")
return
}
defer out.Close()
io.Copy(out, file)
})
// start the server at 127.0.0.1:8080
app.Listen(":8080")
}

View File

@@ -0,0 +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>

View File

@@ -0,0 +1,132 @@
package main
import (
"html/template"
"math/rand"
"net/url"
"sync"
"time"
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
)
func main() {
app := iris.New()
app.Adapt(
iris.DevLogger(),
httprouter.New(),
view.HTML("./templates", ".html").Reload(true),
)
// Serve static files (css)
app.StaticWeb("/static", "./static_files")
var mu sync.Mutex
var urls = map[string]string{
"iris": "http://support.iris-go.com",
}
app.Get("/", func(ctx *iris.Context) {
ctx.Render("index.html", iris.Map{"url_count": len(urls)})
})
// find and execute a short url by its key
// used on http://localhost:8080/url/dsaoj41u321dsa
execShortURL := func(ctx *iris.Context, key string) {
if key == "" {
ctx.EmitError(iris.StatusBadRequest)
return
}
value, found := urls[key]
if !found {
ctx.SetStatusCode(iris.StatusNotFound)
ctx.Writef("Short URL for key: '%s' not found", key)
return
}
ctx.Redirect(value, iris.StatusTemporaryRedirect)
}
app.Get("/url/:shortkey", func(ctx *iris.Context) {
execShortURL(ctx, ctx.Param("shortkey"))
})
// for wildcard subdomain (yeah.. cool) http://dsaoj41u321dsa.localhost:8080
// Note:
// if you want subdomains (chrome doesn't works on localhost, so you have to define other hostname on app.Listen)
// app.Party("*.", func(ctx *iris.Context) {
// execShortURL(ctx, ctx.Subdomain())
// })
app.Post("/url/shorten", func(ctx *iris.Context) {
data := make(map[string]interface{}, 0)
data["url_count"] = len(urls)
value := ctx.FormValue("url")
if value == "" {
data["form_result"] = "You need to a enter a URL."
} else {
urlValue, err := url.ParseRequestURI(value)
if err != nil {
// ctx.JSON(iris.StatusInternalServerError,
// iris.Map{"status": iris.StatusInternalServerError,
// "error": err.Error(),
// "reason": "Invalid URL",
// })
data["form_result"] = "Invalid URL."
} else {
key := randomString(12)
// Make sure that the key is unique
for {
if _, exists := urls[key]; !exists {
break
}
key = randomString(8)
}
mu.Lock()
urls[key] = urlValue.String()
mu.Unlock()
ctx.SetStatusCode(iris.StatusOK)
shortenURL := "http://" + app.Config.VHost + "/url/" + key
data["form_result"] = template.HTML("<pre>Here is your short URL: <a href='" + shortenURL + "'>" + shortenURL + " </a></pre>")
}
}
ctx.Render("index.html", data)
})
app.Listen("localhost:8080")
}
// +------------------------------------------------------------+
// | |
// | Random String |
// | |
// +------------------------------------------------------------+
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
func randomString(n int) string {
src := rand.NewSource(time.Now().UnixNano())
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}

View File

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

View File

@@ -0,0 +1,20 @@
<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="/url/shorten" method="POST">
<input type="text" name="url" style="width: 35em;" />
<input type="submit" value="Shorten!" />
</form>
<p>{{ .url_count }} URLs shortened</p>
</body>
</html>

View File

@@ -0,0 +1,48 @@
package main
import (
"fmt"
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/websocket"
)
func handleConnection(c websocket.Connection) {
// Read events from browser
c.On("chat", func(msg string) {
// Print the message to the console
fmt.Printf("%s sent: %s\n", c.Context().RemoteAddr(), msg)
// Write message back to browser
c.Emit("chat", msg)
})
}
func main() {
app := iris.New()
app.Adapt(iris.DevLogger())
app.Adapt(httprouter.New())
// create our echo websocket server
ws := websocket.New(websocket.Config{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
Endpoint: "/echo",
})
ws.OnConnection(handleConnection)
// Adapt the websocket server.
// you can adapt more than one of course.
app.Adapt(ws)
app.Get("/", func(ctx *iris.Context) {
ctx.ServeFile("websockets.html", false) // second parameter: enable gzip?
})
app.Listen(":8080")
}

View File

@@ -0,0 +1,29 @@
<input id="input" type="text" />
<button onclick="send()">Send</button>
<pre id="output"></pre>
<script src="/iris-ws.js"></script>
<script>
var input = document.getElementById("input");
var output = document.getElementById("output");
// Ws comes from the auto-served '/iris-ws.js'
var socket = new Ws("ws://localhost:8080/echo");
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) {
output.innerHTML += "Server: " + msg + "\n";
});
function send() {
// send chat event data to the server
socket.Emit("chat", input.value);
input.value = "";
}
</script>