mirror of
https://github.com/kataras/iris.git
synced 2025-12-21 11:57:02 +00:00
Add notes for the new lead maintainer of the open-source iris project and align with @get-ion/ion by @hiveminded
Former-commit-id: da4f38eb9034daa49446df3ee529423b98f9b331
This commit is contained in:
172
_examples/routing/basic/main.go
Normal file
172
_examples/routing/basic/main.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
// registers a custom handler for 404 not found http (error) status code,
|
||||
// fires when route not found or manually by ctx.StatusCode(iris.StatusNotFound).
|
||||
app.OnErrorCode(iris.StatusNotFound, notFoundHandler)
|
||||
|
||||
// GET -> HTTP Method
|
||||
// / -> Path
|
||||
// func(ctx context.Context) -> The route's handler.
|
||||
//
|
||||
// Third receiver should contains the route's handler(s), they are executed by order.
|
||||
app.Handle("GET", "/", func(ctx context.Context) {
|
||||
// navigate to the middle of $GOPATH/src/github.com/kataras/iris/context/context.go
|
||||
// to overview all context's method (there a lot of them, read that and you will learn how iris works too)
|
||||
ctx.HTML("Hello from " + ctx.Path()) // Hello from /
|
||||
})
|
||||
|
||||
app.Get("/home", func(ctx context.Context) {
|
||||
ctx.Writef(`Same as app.Handle("GET", "/", [...])`)
|
||||
})
|
||||
|
||||
app.Get("/donate", donateHandler, donateFinishHandler)
|
||||
|
||||
// Pssst, don't forget dynamic-path example for more "magic"!
|
||||
app.Get("/api/users/{userid:int min(1)}", func(ctx context.Context) {
|
||||
userID, err := ctx.Params().GetInt("userid")
|
||||
|
||||
if err != nil {
|
||||
ctx.Writef("error while trying to parse userid parameter," +
|
||||
"this will never happen if :int is being used because if it's not integer it will fire Not Found automatically.")
|
||||
ctx.StatusCode(iris.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(map[string]interface{}{
|
||||
// you can pass any custom structured go value of course.
|
||||
"user_id": userID,
|
||||
})
|
||||
})
|
||||
// app.Post("/", func(ctx context.Context){}) -> for POST http method.
|
||||
// app.Put("/", func(ctx context.Context){})-> for "PUT" http method.
|
||||
// app.Delete("/", func(ctx context.Context){})-> for "DELETE" http method.
|
||||
// app.Options("/", func(ctx context.Context){})-> for "OPTIONS" http method.
|
||||
// app.Trace("/", func(ctx context.Context){})-> for "TRACE" http method.
|
||||
// app.Head("/", func(ctx context.Context){})-> for "HEAD" http method.
|
||||
// app.Connect("/", func(ctx context.Context){})-> for "CONNECT" http method.
|
||||
// app.Patch("/", func(ctx context.Context){})-> for "PATCH" http method.
|
||||
// app.Any("/", func(ctx context.Context){}) for all http methods.
|
||||
|
||||
// More than one route can contain the same path with a different http mapped method.
|
||||
// You can catch any route creation errors with:
|
||||
// route, err := app.Get(...)
|
||||
// set a name to a route: route.Name = "myroute"
|
||||
|
||||
// You can also group routes by path prefix, sharing middleware(s) and done handlers.
|
||||
|
||||
adminRoutes := app.Party("/admin", adminMiddleware)
|
||||
|
||||
adminRoutes.Done(func(ctx context.Context) { // executes always last if ctx.Next()
|
||||
ctx.Application().Logger().Infof("response sent to " + ctx.Path())
|
||||
})
|
||||
// adminRoutes.Layout("/views/layouts/admin.html") // set a view layout for these routes, see more at view examples.
|
||||
|
||||
// GET: http://localhost:8080/admin
|
||||
adminRoutes.Get("/", func(ctx context.Context) {
|
||||
// [...]
|
||||
ctx.StatusCode(iris.StatusOK) // default is 200 == iris.StatusOK
|
||||
ctx.HTML("<h1>Hello from admin/</h1>")
|
||||
|
||||
ctx.Next() // in order to execute the party's "Done" Handler(s)
|
||||
})
|
||||
|
||||
// GET: http://localhost:8080/admin/login
|
||||
adminRoutes.Get("/login", func(ctx context.Context) {
|
||||
// [...]
|
||||
})
|
||||
// POST: http://localhost:8080/admin/login
|
||||
adminRoutes.Post("/login", func(ctx context.Context) {
|
||||
// [...]
|
||||
})
|
||||
|
||||
// subdomains, easier than ever, should add localhost or 127.0.0.1 into your hosts file,
|
||||
// etc/hosts on unix or C:/windows/system32/drivers/etc/hosts on windows.
|
||||
v1 := app.Party("v1.")
|
||||
{ // braces are optional, it's just type of style, to group the routes visually.
|
||||
|
||||
// http://v1.localhost:8080
|
||||
v1.Get("/", func(ctx context.Context) {
|
||||
ctx.HTML("Version 1 API. go to <a href='" + ctx.Path() + "/api" + "'>/api/users</a>")
|
||||
})
|
||||
|
||||
usersAPI := v1.Party("/api/users")
|
||||
{
|
||||
// http://v1.localhost:8080/api/users
|
||||
usersAPI.Get("/", func(ctx context.Context) {
|
||||
ctx.Writef("All users")
|
||||
})
|
||||
// http://v1.localhost:8080/api/users/42
|
||||
usersAPI.Get("/{userid:int}", func(ctx context.Context) {
|
||||
ctx.Writef("user with id: %s", ctx.Params().Get("userid"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// wildcard subdomains.
|
||||
wildcardSubdomain := app.Party("*.")
|
||||
{
|
||||
wildcardSubdomain.Get("/", func(ctx context.Context) {
|
||||
ctx.Writef("Subdomain can be anything, now you're here from: %s", ctx.Subdomain())
|
||||
})
|
||||
}
|
||||
|
||||
// http://localhost:8080
|
||||
// http://localhost:8080/home
|
||||
// http://localhost:8080/donate
|
||||
// http://localhost:8080/api/users/42
|
||||
// http://localhost:8080/admin
|
||||
// http://localhost:8080/admin/login
|
||||
//
|
||||
// http://localhost:8080/api/users/0
|
||||
// http://localhost:8080/api/users/blabla
|
||||
// http://localhost:8080/wontfound
|
||||
//
|
||||
// if hosts edited:
|
||||
// http://v1.localhost:8080
|
||||
// http://v1.localhost:8080/api/users
|
||||
// http://v1.localhost:8080/api/users/42
|
||||
// http://anything.localhost:8080
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
|
||||
func adminMiddleware(ctx context.Context) {
|
||||
// [...]
|
||||
ctx.Next() // to move to the next handler, or don't that if you have any auth logic.
|
||||
}
|
||||
|
||||
func donateHandler(ctx context.Context) {
|
||||
ctx.Writef("Just like an inline handler, but it can be " +
|
||||
"used by other package, anywhere in your project.")
|
||||
|
||||
// let's pass a value to the next handler
|
||||
// Values is the way handlers(or middleware) are communicating between each other.
|
||||
ctx.Values().Set("donate_url", "https://github.com/kataras/iris#buy-me-a-cup-of-coffee")
|
||||
ctx.Next() // in order to execute the next handler in the chain, look donate route.
|
||||
}
|
||||
|
||||
func donateFinishHandler(ctx context.Context) {
|
||||
// values can be any type of object so we could cast the value to a string
|
||||
// but iris provides an easy to do that, if donate_url is not defined, then it returns an empty string instead.
|
||||
donateURL := ctx.Values().GetString("donate_url")
|
||||
ctx.Application().Logger().Infof("donate_url value was: " + donateURL)
|
||||
ctx.Writef("\n\nDonate sent(?).")
|
||||
}
|
||||
|
||||
func notFoundHandler(ctx context.Context) {
|
||||
ctx.HTML("Custom route for 404 not found http code, here you can render a view, html, json <b>any valid response</b>.")
|
||||
}
|
||||
|
||||
// Notes:
|
||||
// A path parameter name should contain only alphabetical letters, symbols, containing '_' and numbers are NOT allowed.
|
||||
// If route failed to be registered, the app will panic without any warnings
|
||||
// if you didn't catch the second return value(error) on .Handle/.Get....
|
||||
|
||||
// See "file-server/single-page-application" to see how another feature, "WrapRouter", works.
|
||||
79
_examples/routing/custom-context/method-overriding/main.go
Normal file
79
_examples/routing/custom-context/method-overriding/main.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
// In this package I'll show you how to override the existing Context's functions and methods.
|
||||
// You can easly navigate to the custom-context example to see how you can add new functions
|
||||
// to your own context (need a custom handler).
|
||||
//
|
||||
// This way is far easier to understand and it's faster when you want to override existing methods:
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
// Create your own custom Context, put any fields you wanna need.
|
||||
type MyContext struct {
|
||||
// Optional Part 1: embed (optional but required if you don't want to override all context's methods)
|
||||
context.Context // it's the context/context.go#context struct but you don't need to know it.
|
||||
}
|
||||
|
||||
var _ context.Context = &MyContext{} // optionally: validate on compile-time if MyContext implements context.Context.
|
||||
|
||||
// Optional Part 2:
|
||||
// The only one important if you will override the Context with an embedded context.Context inside it.
|
||||
func (ctx *MyContext) Next() {
|
||||
context.Next(ctx)
|
||||
}
|
||||
|
||||
// Override any context's method you want...
|
||||
// [...]
|
||||
|
||||
func (ctx *MyContext) HTML(htmlContents string) (int, error) {
|
||||
ctx.Application().Logger().Infof("Executing .HTML function from MyContext")
|
||||
|
||||
ctx.ContentType("text/html")
|
||||
return ctx.WriteString(htmlContents)
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
// Register a view engine on .html files inside the ./view/** directory.
|
||||
app.RegisterView(iris.HTML("./view", ".html"))
|
||||
|
||||
// The only one Required:
|
||||
// here is how you define how your own context will be created and acquired from the iris' generic context pool.
|
||||
app.ContextPool.Attach(func() context.Context {
|
||||
return &MyContext{
|
||||
// Optional Part 3:
|
||||
Context: context.NewContext(app),
|
||||
}
|
||||
})
|
||||
|
||||
// register your route, as you normally do
|
||||
app.Handle("GET", "/", recordWhichContextJustForProofOfConcept, func(ctx context.Context) {
|
||||
// use the context's overridden HTML method.
|
||||
ctx.HTML("<h1> Hello from my custom context's HTML! </h1>")
|
||||
})
|
||||
|
||||
// this will be executed by the MyContext.Context
|
||||
// if MyContext is not directly define the View function by itself.
|
||||
app.Handle("GET", "/hi/{firstname:alphabetical}", recordWhichContextJustForProofOfConcept, func(ctx context.Context) {
|
||||
firstname := ctx.Values().GetString("firstname")
|
||||
|
||||
ctx.ViewData("firstname", firstname)
|
||||
ctx.Gzip(true)
|
||||
|
||||
ctx.View("hi.html")
|
||||
})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
|
||||
// should always print "($PATH) Handler is executing from 'MyContext'"
|
||||
func recordWhichContextJustForProofOfConcept(ctx context.Context) {
|
||||
ctx.Application().Logger().Infof("(%s) Handler is executing from: '%s'", ctx.Path(), reflect.TypeOf(ctx).Elem().Name())
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
// Look "new-implementation" to see how you can create an entirely new Context with new functions.
|
||||
@@ -0,0 +1 @@
|
||||
<h1> Hi {{.firstname}} </h1>
|
||||
104
_examples/routing/custom-context/new-implementation/main.go
Normal file
104
_examples/routing/custom-context/new-implementation/main.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/sessions"
|
||||
)
|
||||
|
||||
// Owner is our application structure, it contains the methods or fields we need,
|
||||
// think it as the owner of our *Context.
|
||||
type Owner struct {
|
||||
// define here the fields that are global
|
||||
// and shared to all clients.
|
||||
sessionsManager *sessions.Sessions
|
||||
}
|
||||
|
||||
// this package-level variable "application" will be used inside context to communicate with our global Application.
|
||||
var owner = &Owner{
|
||||
sessionsManager: sessions.New(sessions.Config{Cookie: "mysessioncookie"}),
|
||||
}
|
||||
|
||||
// Context is our custom context.
|
||||
// Let's implement a context which will give us access
|
||||
// to the client's Session with a trivial `ctx.Session()` call.
|
||||
type Context struct {
|
||||
context.Context
|
||||
session *sessions.Session
|
||||
}
|
||||
|
||||
// Session returns the current client's session.
|
||||
func (ctx *Context) Session() *sessions.Session {
|
||||
// this help us if we call `Session()` multiple times in the same handler
|
||||
if ctx.session == nil {
|
||||
// start a new session if not created before.
|
||||
ctx.session = owner.sessionsManager.Start(ctx.Context)
|
||||
}
|
||||
|
||||
return ctx.session
|
||||
}
|
||||
|
||||
// Bold will send a bold text to the client.
|
||||
func (ctx *Context) Bold(text string) {
|
||||
ctx.HTML("<b>" + text + "</b>")
|
||||
}
|
||||
|
||||
var contextPool = sync.Pool{New: func() interface{} {
|
||||
return &Context{}
|
||||
}}
|
||||
|
||||
func acquire(original context.Context) *Context {
|
||||
ctx := contextPool.Get().(*Context)
|
||||
ctx.Context = original // set the context to the original one in order to have access to iris's implementation.
|
||||
ctx.session = nil // reset the session
|
||||
return ctx
|
||||
}
|
||||
|
||||
func release(ctx *Context) {
|
||||
contextPool.Put(ctx)
|
||||
}
|
||||
|
||||
// Handler will convert our handler of func(*Context) to an iris Handler,
|
||||
// in order to be compatible with the HTTP API.
|
||||
func Handler(h func(*Context)) context.Handler {
|
||||
return func(original context.Context) {
|
||||
ctx := acquire(original)
|
||||
h(ctx)
|
||||
release(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
|
||||
// Work as you did before, the only difference
|
||||
// is that the original context.Handler should be wrapped with our custom
|
||||
// `Handler` function.
|
||||
app.Get("/", Handler(func(ctx *Context) {
|
||||
ctx.Bold("Hello from our *Context")
|
||||
}))
|
||||
|
||||
app.Post("/set", Handler(func(ctx *Context) {
|
||||
nameFieldValue := ctx.FormValue("name")
|
||||
ctx.Session().Set("name", nameFieldValue)
|
||||
ctx.Writef("set session = " + nameFieldValue)
|
||||
}))
|
||||
|
||||
app.Get("/get", Handler(func(ctx *Context) {
|
||||
name := ctx.Session().GetString("name")
|
||||
ctx.Writef(name)
|
||||
}))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := newApp()
|
||||
|
||||
// GET: http://localhost:8080
|
||||
// POST: http://localhost:8080/set
|
||||
// GET: http://localhost:8080/get
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/httptest"
|
||||
)
|
||||
|
||||
func TestCustomContextNewImpl(t *testing.T) {
|
||||
app := newApp()
|
||||
e := httptest.New(t, app, httptest.URL("http://localhost:8080"))
|
||||
|
||||
e.GET("/").Expect().
|
||||
Status(httptest.StatusOK).
|
||||
ContentType("text/html").
|
||||
Body().Equal("<b>Hello from our *Context</b>")
|
||||
|
||||
expectedName := "iris"
|
||||
e.POST("/set").WithFormField("name", expectedName).Expect().
|
||||
Status(httptest.StatusOK).
|
||||
Body().Equal("set session = " + expectedName)
|
||||
|
||||
e.GET("/get").Expect().
|
||||
Status(httptest.StatusOK).
|
||||
Body().Equal(expectedName)
|
||||
}
|
||||
92
_examples/routing/custom-wrapper/main.go
Normal file
92
_examples/routing/custom-wrapper/main.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
// In this example you'll just see one use case of .WrapRouter.
|
||||
// You can use the .WrapRouter to add custom logic when or when not the router should
|
||||
// be executed in order to execute the registered routes' handlers.
|
||||
//
|
||||
// To see how you can serve files on root "/" without a custom wrapper
|
||||
// just navigate to the "file-server/single-page-application" example.
|
||||
//
|
||||
// This is just for the proof of concept, you can skip this tutorial if it's too much for you.
|
||||
func newApp() *iris.Application {
|
||||
|
||||
app := iris.New()
|
||||
|
||||
app.OnErrorCode(iris.StatusNotFound, func(ctx context.Context) {
|
||||
ctx.HTML("<b>Resource Not found</b>")
|
||||
})
|
||||
|
||||
app.Get("/", func(ctx context.Context) {
|
||||
ctx.ServeFile("./public/index.html", false)
|
||||
})
|
||||
|
||||
app.Get("/profile/{username}", func(ctx context.Context) {
|
||||
ctx.Writef("Hello %s", ctx.Params().Get("username"))
|
||||
})
|
||||
|
||||
// serve files from the root "/", if we used .StaticWeb it could override
|
||||
// all the routes because of the underline need of wildcard.
|
||||
// Here we will see how you can by-pass this behavior
|
||||
// by creating a new file server handler and
|
||||
// setting up a wrapper for the router(like a "low-level" middleware)
|
||||
// in order to manually check if we want to process with the router as normally
|
||||
// or execute the file server handler instead.
|
||||
|
||||
// use of the .StaticHandler
|
||||
// which is the same as StaticWeb but it doesn't
|
||||
// registers the route, it just returns the handler.
|
||||
fileServer := app.StaticHandler("./public", false, false)
|
||||
|
||||
// wrap the router with a native net/http handler.
|
||||
// if url does not contain any "." (i.e: .css, .js...)
|
||||
// (depends on the app , you may need to add more file-server exceptions),
|
||||
// then the handler will execute the router that is responsible for the
|
||||
// registered routes (look "/" and "/profile/{username}")
|
||||
// if not then it will serve the files based on the root "/" path.
|
||||
app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
|
||||
path := r.URL.Path
|
||||
// Note that if path has suffix of "index.html" it will auto-permant redirect to the "/",
|
||||
// so our first handler will be executed instead.
|
||||
|
||||
if !strings.Contains(path, ".") { // if it's not a resource then continue to the router as normally.
|
||||
router(w, r)
|
||||
return
|
||||
}
|
||||
// acquire and release a context in order to use it to execute
|
||||
// our file server
|
||||
// remember: we use net/http.Handler because here we are in the "low-level", before the router itself.
|
||||
ctx := app.ContextPool.Acquire(w, r)
|
||||
fileServer(ctx)
|
||||
app.ContextPool.Release(ctx)
|
||||
})
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := newApp()
|
||||
|
||||
// http://localhost:8080
|
||||
// http://localhost:8080/index.html
|
||||
// http://localhost:8080/app.js
|
||||
// http://localhost:8080/css/main.css
|
||||
// http://localhost:8080/profile/anyusername
|
||||
app.Run(iris.Addr(":8080"))
|
||||
|
||||
// Note: In this example we just saw one use case,
|
||||
// you may want to .WrapRouter or .Downgrade in order to bypass the iris' default router, i.e:
|
||||
// you can use that method to setup custom proxies too.
|
||||
//
|
||||
// If you just want to serve static files on other path than root
|
||||
// you can just use the StaticWeb, i.e:
|
||||
// .StaticWeb("/static", "./public")
|
||||
// ________________________________requestPath, systemPath
|
||||
}
|
||||
59
_examples/routing/custom-wrapper/main_test.go
Normal file
59
_examples/routing/custom-wrapper/main_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/httptest"
|
||||
)
|
||||
|
||||
type resource string
|
||||
|
||||
func (r resource) String() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
func (r resource) strip(strip string) string {
|
||||
s := r.String()
|
||||
return strings.TrimPrefix(s, strip)
|
||||
}
|
||||
|
||||
func (r resource) loadFromBase(dir string) string {
|
||||
filename := r.String()
|
||||
|
||||
if filename == "/" {
|
||||
filename = "/index.html"
|
||||
}
|
||||
|
||||
fullpath := filepath.Join(dir, filename)
|
||||
|
||||
b, err := ioutil.ReadFile(fullpath)
|
||||
if err != nil {
|
||||
panic(fullpath + " failed with error: " + err.Error())
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
||||
var urls = []resource{
|
||||
"/",
|
||||
"/index.html",
|
||||
"/app.js",
|
||||
"/css/main.css",
|
||||
}
|
||||
|
||||
func TestCustomWrapper(t *testing.T) {
|
||||
app := newApp()
|
||||
e := httptest.New(t, app)
|
||||
|
||||
for _, u := range urls {
|
||||
url := u.String()
|
||||
contents := u.loadFromBase("./public")
|
||||
|
||||
e.GET(url).Expect().
|
||||
Status(httptest.StatusOK).
|
||||
Body().Equal(contents)
|
||||
}
|
||||
}
|
||||
1
_examples/routing/custom-wrapper/public/app.js
Normal file
1
_examples/routing/custom-wrapper/public/app.js
Normal file
@@ -0,0 +1 @@
|
||||
window.alert("app.js loaded from \"/");
|
||||
3
_examples/routing/custom-wrapper/public/css/main.css
Normal file
3
_examples/routing/custom-wrapper/public/css/main.css
Normal file
@@ -0,0 +1,3 @@
|
||||
body {
|
||||
background-color: black;
|
||||
}
|
||||
14
_examples/routing/custom-wrapper/public/index.html
Normal file
14
_examples/routing/custom-wrapper/public/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{ .Page.Title }}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1> Hello from index.html </h1>
|
||||
|
||||
|
||||
<script src="/app.js"> </script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
183
_examples/routing/dynamic-path/main.go
Normal file
183
_examples/routing/dynamic-path/main.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
// At the previous example "routing/basic",
|
||||
// we've seen static routes, group of routes, subdomains, wildcard subdomains, a small example of parameterized path
|
||||
// with a single known paramete and custom http errors, now it's time to see wildcard parameters and macros.
|
||||
|
||||
// iris, like net/http std package registers route's handlers
|
||||
// by a Handler, the iris' type of handler is just a func(ctx context.Context)
|
||||
// where context comes from github.com/kataras/iris/context.
|
||||
// Until go 1.9 you will have to import that package too, after go 1.9 this will be not be necessary.
|
||||
//
|
||||
// iris has the easiest and the most powerful routing process you have ever meet.
|
||||
//
|
||||
// At the same time,
|
||||
// iris has its own interpeter(yes like a programming language)
|
||||
// for route's path syntax and their dynamic path parameters parsing and evaluation,
|
||||
// I am calling them "macros" for shortcut.
|
||||
// How? It calculates its needs and if not any special regexp needed then it just
|
||||
// registers the route with the low-level underline path syntax,
|
||||
// otherwise it pre-compiles the regexp and adds the necessary middleware(s).
|
||||
//
|
||||
// Standard macro types for parameters:
|
||||
// +------------------------+
|
||||
// | {param:string} |
|
||||
// +------------------------+
|
||||
// string type
|
||||
// anything
|
||||
//
|
||||
// +------------------------+
|
||||
// | {param:int} |
|
||||
// +------------------------+
|
||||
// int type
|
||||
// only numbers (0-9)
|
||||
//
|
||||
// +------------------------+
|
||||
// | {param:alphabetical} |
|
||||
// +------------------------+
|
||||
// alphabetical/letter type
|
||||
// letters only (upper or lowercase)
|
||||
//
|
||||
// +------------------------+
|
||||
// | {param:file} |
|
||||
// +------------------------+
|
||||
// file type
|
||||
// letters (upper or lowercase)
|
||||
// numbers (0-9)
|
||||
// underscore (_)
|
||||
// dash (-)
|
||||
// point (.)
|
||||
// no spaces ! or other character
|
||||
//
|
||||
// +------------------------+
|
||||
// | {param:path} |
|
||||
// +------------------------+
|
||||
// path type
|
||||
// anything, should be the last part, more than one path segment,
|
||||
// i.e: /path1/path2/path3 , ctx.Params().GetString("param") == "/path1/path2/path3"
|
||||
//
|
||||
// if type is missing then parameter's type is defaulted to string, so
|
||||
// {param} == {param:string}.
|
||||
//
|
||||
// If a function not found on that type then the "string"'s types functions are being used.
|
||||
// i.e:
|
||||
// {param:int min(3)}
|
||||
//
|
||||
//
|
||||
// Besides the fact that iris provides the basic types and some default "macro funcs"
|
||||
// you are able to register your own too!.
|
||||
//
|
||||
// Register a named path parameter function:
|
||||
// app.Macros().Int.RegisterFunc("min", func(argument int) func(paramValue string) bool {
|
||||
// [...]
|
||||
// return true/false -> true means valid.
|
||||
// })
|
||||
//
|
||||
// at the func(argument ...) you can have any standard type, it will be validated before the server starts
|
||||
// so don't care about performance here, the only thing it runs at serve time is the returning func(paramValue string) bool.
|
||||
//
|
||||
// {param:string equal(iris)} , "iris" will be the argument here:
|
||||
// app.Macros().String.RegisterFunc("equal", func(argument string) func(paramValue string) bool {
|
||||
// return func(paramValue string){ return argument == paramValue }
|
||||
// })
|
||||
|
||||
// you can use the "string" type which is valid for a single path parameter that can be anything.
|
||||
app.Get("/username/{name}", func(ctx context.Context) {
|
||||
ctx.Writef("Hello %s", ctx.Params().Get("name"))
|
||||
}) // type is missing = {name:string}
|
||||
|
||||
// Let's register our first macro attached to int macro type.
|
||||
// "min" = the function
|
||||
// "minValue" = the argument of the function
|
||||
// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
|
||||
// a user requests a path which contains the :int macro type with the min(...) macro parameter function.
|
||||
app.Macros().Int.RegisterFunc("min", func(minValue int) func(string) bool {
|
||||
// do anything before serve here [...]
|
||||
// at this case we don't need to do anything
|
||||
return func(paramValue string) bool {
|
||||
n, err := strconv.Atoi(paramValue)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return n >= minValue
|
||||
}
|
||||
})
|
||||
|
||||
// http://localhost:8080/profile/id>=1
|
||||
// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
|
||||
// macro parameter functions are optional of course.
|
||||
app.Get("/profile/{id:int min(1)}", func(ctx context.Context) {
|
||||
// second parameter is the error but it will always nil because we use macros,
|
||||
// the validaton already happened.
|
||||
id, _ := ctx.Params().GetInt("id")
|
||||
ctx.Writef("Hello id: %d", id)
|
||||
})
|
||||
|
||||
// to change the error code per route's macro evaluator:
|
||||
app.Get("/profile/{id:int min(1)}/friends/{friendid:int min(1) else 504}", func(ctx context.Context) {
|
||||
id, _ := ctx.Params().GetInt("id")
|
||||
friendid, _ := ctx.Params().GetInt("friendid")
|
||||
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
|
||||
}) // this will throw e 504 error code instead of 404 if all route's macros not passed.
|
||||
|
||||
// http://localhost:8080/game/a-zA-Z/level/0-9
|
||||
// remember, alphabetical is lowercase or uppercase letters only.
|
||||
app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx context.Context) {
|
||||
ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level"))
|
||||
})
|
||||
|
||||
app.Get("/lowercase/static", func(ctx context.Context) {
|
||||
ctx.Writef("static and dynamic paths are not conflicted anymore!")
|
||||
})
|
||||
|
||||
// let's use a trivial custom regexp that validates a single path parameter
|
||||
// which its value is only lowercase letters.
|
||||
|
||||
// http://localhost:8080/lowercase/anylowercase
|
||||
app.Get("/lowercase/{name:string regexp(^[a-z]+)}", func(ctx context.Context) {
|
||||
ctx.Writef("name should be only lowercase, otherwise this handler will never executed: %s", ctx.Params().Get("name"))
|
||||
})
|
||||
|
||||
// http://localhost:8080/single_file/app.js
|
||||
app.Get("/single_file/{myfile:file}", func(ctx context.Context) {
|
||||
ctx.Writef("file type validates if the parameter value has a form of a file name, got: %s", ctx.Params().Get("myfile"))
|
||||
})
|
||||
|
||||
// http://localhost:8080/myfiles/any/directory/here/
|
||||
// this is the only macro type that accepts any number of path segments.
|
||||
app.Get("/myfiles/{directory:path}", func(ctx context.Context) {
|
||||
ctx.Writef("path type accepts any number of path segments, path after /myfiles/ is: %s", ctx.Params().Get("directory"))
|
||||
}) // for wildcard path (any number of path segments) without validation you can use:
|
||||
// /myfiles/*
|
||||
|
||||
// "{param}"'s performance is exactly the same of ":param"'s.
|
||||
|
||||
// alternatives -> ":param" for single path parameter and "*" for wildcard path parameter.
|
||||
// Note these:
|
||||
// if "/mypath/*" then the parameter name is "*".
|
||||
// if "/mypath/{myparam:path}" then the parameter has two names, one is the "*" and the other is the user-defined "myparam".
|
||||
|
||||
// WARNING:
|
||||
// A path parameter name should contain only alphabetical letters, symbols, containing '_' and numbers are NOT allowed.
|
||||
// If route failed to be registered, the app will panic without any warnings
|
||||
// if you didn't catch the second return value(error) on .Handle/.Get....
|
||||
|
||||
// Last, do not confuse ctx.Values() with ctx.Params().
|
||||
// Path parameter's values goes to ctx.Params() and context's local storage
|
||||
// that can be used to communicate between handlers and middleware(s) goes to
|
||||
// ctx.Values(), path parameters and the rest of any custom values are separated for your own good.
|
||||
|
||||
if err := app.Run(iris.Addr(":8080")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
29
_examples/routing/http-errors/main.go
Normal file
29
_examples/routing/http-errors/main.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
app.OnErrorCode(iris.StatusInternalServerError, func(ctx context.Context) {
|
||||
ctx.HTML("Message: <b>" + ctx.Values().GetString("message") + "</b>")
|
||||
})
|
||||
|
||||
app.Get("/", func(ctx context.Context) {
|
||||
ctx.HTML(`Click <a href="/my500">here</a> to fire the 500 status code`)
|
||||
})
|
||||
|
||||
app.Get("/my500", func(ctx context.Context) {
|
||||
ctx.Values().Set("message", "this is the error message")
|
||||
ctx.StatusCode(500)
|
||||
})
|
||||
|
||||
app.Get("/u/{firstname:alphabetical}", func(ctx context.Context) {
|
||||
ctx.Writef("Hello %s", ctx.Params().Get("firstname"))
|
||||
})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
145
_examples/routing/main.go
Normal file
145
_examples/routing/main.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
/*
|
||||
Read:
|
||||
"overview"
|
||||
"basic"
|
||||
"dynamic-path"
|
||||
and "reverse" examples if you want to release iris' real power.
|
||||
*/
|
||||
|
||||
const maxBodySize = 1 << 20
|
||||
const notFoundHTML = "<h1> custom http error page </h1>"
|
||||
|
||||
func registerErrors(app *iris.Application) {
|
||||
// set a custom 404 handler
|
||||
app.OnErrorCode(iris.StatusNotFound, func(ctx context.Context) {
|
||||
ctx.HTML(notFoundHTML)
|
||||
})
|
||||
}
|
||||
|
||||
func registerGamesRoutes(app *iris.Application) {
|
||||
gamesMiddleware := func(ctx context.Context) {
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
// party is just a group of routes with the same prefix
|
||||
// and middleware, i.e: "/games" and gamesMiddleware.
|
||||
games := app.Party("/games", gamesMiddleware)
|
||||
{ // braces are optional of course, it's just a style of code
|
||||
|
||||
// "GET" method
|
||||
games.Get("/{gameID:int}/clans", h)
|
||||
games.Get("/{gameID:int}/clans/clan/{clanPublicID:int}", h)
|
||||
games.Get("/{gameID:int}/clans/search", h)
|
||||
|
||||
// "PUT" method
|
||||
games.Put("/{gameID:int}/players/{clanPublicID:int}", h)
|
||||
games.Put("/{gameID:int}/clans/clan/{clanPublicID:int}", h)
|
||||
// remember: "clanPublicID" should not be changed to other routes with the same prefix.
|
||||
// "POST" method
|
||||
games.Post("/{gameID:int}/clans", h)
|
||||
games.Post("/{gameID:int}/players", h)
|
||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/leave", h)
|
||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/application", h)
|
||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/application/{action}", h) // {action} == {action:string}
|
||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/invitation", h)
|
||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/invitation/{action}", h)
|
||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/delete", h)
|
||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/promote", h)
|
||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/demote", h)
|
||||
}
|
||||
}
|
||||
|
||||
func registerSubdomains(app *iris.Application) {
|
||||
mysubdomain := app.Party("mysubdomain.")
|
||||
// http://mysubdomain.myhost.com
|
||||
mysubdomain.Get("/", h)
|
||||
}
|
||||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
registerErrors(app)
|
||||
registerGamesRoutes(app)
|
||||
registerSubdomains(app)
|
||||
|
||||
app.Handle("GET", "/healthcheck", h)
|
||||
|
||||
// "POST" method
|
||||
// this handler reads raw body from the client/request
|
||||
// and sends back the same body
|
||||
// remember, we have limit to that body in order
|
||||
// to protect ourselves from "over heating".
|
||||
app.Post("/", context.LimitRequestBodySize(maxBodySize), func(ctx context.Context) {
|
||||
// get request body
|
||||
b, err := ioutil.ReadAll(ctx.Request().Body)
|
||||
// if is larger then send a bad request status
|
||||
if err != nil {
|
||||
ctx.StatusCode(iris.StatusBadRequest)
|
||||
ctx.Writef(err.Error())
|
||||
return
|
||||
}
|
||||
// send back the post body
|
||||
ctx.Write(b)
|
||||
})
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func h(ctx context.Context) {
|
||||
method := ctx.Method() // the http method requested a server's resource.
|
||||
subdomain := ctx.Subdomain() // the subdomain, if any.
|
||||
|
||||
// the request path (without scheme and host).
|
||||
path := ctx.Path()
|
||||
// how to get all parameters, if we don't know
|
||||
// the names:
|
||||
paramsLen := ctx.Params().Len()
|
||||
|
||||
ctx.Params().Visit(func(name string, value string) {
|
||||
ctx.Writef("%s = %s\n", name, value)
|
||||
})
|
||||
ctx.Writef("Info\n\n")
|
||||
ctx.Writef("Method: %s\nSubdomain: %s\nPath: %s\nParameters length: %d", method, subdomain, path, paramsLen)
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := newApp()
|
||||
|
||||
/*
|
||||
// GET
|
||||
http://localhost:8080/healthcheck
|
||||
http://localhost:8080/games/42/clans
|
||||
http://localhost:8080/games/42/clans/clan/93
|
||||
http://localhost:8080/games/42/clans/search
|
||||
http://mysubdomain.localhost:8080/
|
||||
|
||||
// PUT
|
||||
http://localhost:8080/games/42/players/93
|
||||
http://localhost:8080/games/42/clans/clan/93
|
||||
|
||||
// POST
|
||||
http://localhost:8080/
|
||||
http://localhost:8080/games/42/clans
|
||||
http://localhost:8080/games/42/players
|
||||
http://localhost:8080/games/42/clans/93/leave
|
||||
http://localhost:8080/games/42/clans/93/memberships/application
|
||||
http://localhost:8080/games/42/clans/93/memberships/application/anystring
|
||||
http://localhost:8080/games/42/clans/93/memberships/invitation
|
||||
http://localhost:8080/games/42/clans/93/memberships/invitation/anystring
|
||||
http://localhost:8080/games/42/clans/93/memberships/delete
|
||||
http://localhost:8080/games/42/clans/93/memberships/promote
|
||||
http://localhost:8080/games/42/clans/93/memberships/demote
|
||||
|
||||
// FIRE NOT FOUND
|
||||
http://localhost:8080/coudlntfound
|
||||
*/
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
125
_examples/routing/main_test.go
Normal file
125
_examples/routing/main_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/httptest"
|
||||
)
|
||||
|
||||
func calculatePathAndResponse(method, subdomain, path string, paramKeyValue ...string) (string, string) {
|
||||
paramsLen := 0
|
||||
|
||||
if l := len(paramKeyValue); l >= 2 {
|
||||
paramsLen = len(paramKeyValue) / 2
|
||||
}
|
||||
|
||||
paramsInfo := ""
|
||||
if paramsLen > 0 {
|
||||
for i := 0; i < len(paramKeyValue); i++ {
|
||||
paramKey := paramKeyValue[i]
|
||||
i++
|
||||
if i >= len(paramKeyValue) {
|
||||
panic("paramKeyValue should be align with path parameters {} and must be placed in order")
|
||||
}
|
||||
|
||||
paramValue := paramKeyValue[i]
|
||||
paramsInfo += paramKey + " = " + paramValue + "\n"
|
||||
|
||||
beginParam := strings.IndexByte(path, '{')
|
||||
endParam := strings.IndexByte(path, '}')
|
||||
if beginParam == -1 || endParam == -1 {
|
||||
panic("something wrong with parameters, please define them in order")
|
||||
}
|
||||
|
||||
path = path[:beginParam] + paramValue + path[endParam+1:]
|
||||
}
|
||||
}
|
||||
|
||||
return path, paramsInfo + `Info
|
||||
|
||||
Method: ` + method + `
|
||||
Subdomain: ` + subdomain + `
|
||||
Path: ` + path + `
|
||||
Parameters length: ` + strconv.Itoa(paramsLen)
|
||||
}
|
||||
|
||||
type troute struct {
|
||||
method, subdomain, path string
|
||||
status int
|
||||
expectedBody string
|
||||
contentType string
|
||||
}
|
||||
|
||||
func newTroute(method, subdomain, path string, status int, paramKeyValue ...string) troute {
|
||||
finalPath, expectedBody := calculatePathAndResponse(method, subdomain, path, paramKeyValue...)
|
||||
contentType := "text/plain; charset=UTF-8"
|
||||
|
||||
if status == httptest.StatusNotFound {
|
||||
expectedBody = notFoundHTML
|
||||
contentType = "text/html; charset=UTF-8"
|
||||
}
|
||||
|
||||
return troute{
|
||||
contentType: contentType,
|
||||
method: method,
|
||||
subdomain: subdomain,
|
||||
path: finalPath,
|
||||
status: status,
|
||||
expectedBody: expectedBody,
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouting(t *testing.T) {
|
||||
app := newApp()
|
||||
e := httptest.New(t, app)
|
||||
|
||||
var tests = []troute{
|
||||
// GET
|
||||
newTroute("GET", "", "/healthcheck", httptest.StatusOK),
|
||||
newTroute("GET", "", "/games/{gameID}/clans", httptest.StatusOK, "gameID", "42"),
|
||||
newTroute("GET", "", "/games/{gameID}/clans/clan/{clanPublicID}", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"),
|
||||
newTroute("GET", "", "/games/{gameID}/clans/search", httptest.StatusOK, "gameID", "42"),
|
||||
newTroute("GET", "mysubdomain", "/", httptest.StatusOK),
|
||||
// PUT
|
||||
newTroute("PUT", "", "/games/{gameID}/players/{clanPublicID}", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"),
|
||||
newTroute("PUT", "", "/games/{gameID}/clans/clan/{clanPublicID}", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"),
|
||||
// POST
|
||||
newTroute("POST", "", "/games/{gameID}/clans", httptest.StatusOK, "gameID", "42"),
|
||||
newTroute("POST", "", "/games/{gameID}/players", httptest.StatusOK, "gameID", "42"),
|
||||
newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/leave", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"),
|
||||
newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/application", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"),
|
||||
newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/application/{action}", httptest.StatusOK, "gameID", "42", "clanPublicID", "93", "action", "somethinghere"),
|
||||
newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/invitation", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"),
|
||||
newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/invitation/{action}", httptest.StatusOK, "gameID", "42", "clanPublicID", "93", "action", "somethinghere"),
|
||||
newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/delete", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"),
|
||||
newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/promote", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"),
|
||||
newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/demote", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"),
|
||||
// POST: / will be tested alone
|
||||
// custom not found
|
||||
newTroute("GET", "", "/notfound", httptest.StatusNotFound),
|
||||
newTroute("POST", "", "/notfound2", httptest.StatusNotFound),
|
||||
newTroute("PUT", "", "/notfound3", httptest.StatusNotFound),
|
||||
newTroute("GET", "mysubdomain", "/notfound42", httptest.StatusNotFound),
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
et := e.Request(tt.method, tt.path)
|
||||
if tt.subdomain != "" {
|
||||
et.WithURL("http://" + tt.subdomain + ".localhost:8080")
|
||||
}
|
||||
et.Expect().Status(tt.status).Body().Equal(tt.expectedBody)
|
||||
}
|
||||
|
||||
// test POST "/" limit data and post data return
|
||||
|
||||
// test with small body
|
||||
e.POST("/").WithBytes([]byte("ok")).Expect().Status(httptest.StatusOK).Body().Equal("ok")
|
||||
// test with equal to max body size limit
|
||||
bsent := make([]byte, maxBodySize, maxBodySize)
|
||||
e.POST("/").WithBytes(bsent).Expect().Status(httptest.StatusOK).Body().Length().Equal(len(bsent))
|
||||
// test with larger body sent and wait for the custom response
|
||||
largerBSent := make([]byte, maxBodySize+1, maxBodySize+1)
|
||||
e.POST("/").WithBytes(largerBSent).Expect().Status(httptest.StatusBadRequest).Body().Equal("http: request body too large")
|
||||
}
|
||||
136
_examples/routing/overview/main.go
Normal file
136
_examples/routing/overview/main.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
// GET: http://localhost:8080
|
||||
app.Get("/", info)
|
||||
|
||||
// GET: http://localhost:8080/profile/anyusername
|
||||
app.Get("/profile/{username:string}", info)
|
||||
// GET: http://localhost:8080/profile/anyusername/backups/any/number/of/paths/here
|
||||
app.Get("/profile/{username:string}/backups/{filepath:path}", info)
|
||||
// Favicon
|
||||
|
||||
// GET: http://localhost:8080/favicon.ico
|
||||
app.Favicon("./public/images/favicon.ico")
|
||||
|
||||
// Static assets
|
||||
|
||||
// GET: http://localhost:8080/assets/css/bootstrap.min.css
|
||||
// maps to ./public/assets/css/bootstrap.min.css file at system location.
|
||||
// GET: http://localhost:8080/assets/js/react.min.js
|
||||
// maps to ./public/assets/js/react.min.js file at system location.
|
||||
app.StaticWeb("/assets", "./public/assets")
|
||||
|
||||
/* OR
|
||||
|
||||
// GET: http://localhost:8080/js/react.min.js
|
||||
// maps to ./public/assets/js/react.min.js file at system location.
|
||||
app.StaticWeb("/js", "./public/assets/js")
|
||||
|
||||
// GET: http://localhost:8080/css/bootstrap.min.css
|
||||
// maps to ./public/assets/css/bootstrap.min.css file at system location.
|
||||
app.StaticWeb("/css", "./public/assets/css")
|
||||
|
||||
*/
|
||||
|
||||
// Grouping
|
||||
|
||||
usersRoutes := app.Party("/users")
|
||||
// GET: http://localhost:8080/users/help
|
||||
usersRoutes.Get("/help", func(ctx context.Context) {
|
||||
ctx.Writef("GET / -- fetch all users\n")
|
||||
ctx.Writef("GET /$ID -- fetch a user by id\n")
|
||||
ctx.Writef("POST / -- create new user\n")
|
||||
ctx.Writef("PUT /$ID -- update an existing user\n")
|
||||
ctx.Writef("DELETE /$ID -- delete an existing user\n")
|
||||
})
|
||||
|
||||
// GET: http://localhost:8080/users
|
||||
usersRoutes.Get("/", func(ctx context.Context) {
|
||||
ctx.Writef("get all users")
|
||||
})
|
||||
|
||||
// GET: http://localhost:8080/users/42
|
||||
// **/users/42 and /users/help works after iris version 7.0.5**
|
||||
usersRoutes.Get("/{id:int}", func(ctx context.Context) {
|
||||
id, _ := ctx.Params().GetInt("id")
|
||||
ctx.Writef("get user by id: %d", id)
|
||||
})
|
||||
|
||||
// POST: http://localhost:8080/users
|
||||
usersRoutes.Post("/", func(ctx context.Context) {
|
||||
username, password := ctx.PostValue("username"), ctx.PostValue("password")
|
||||
ctx.Writef("create user for username= %s and password= %s", username, password)
|
||||
})
|
||||
|
||||
// PUT: http://localhost:8080/users
|
||||
usersRoutes.Put("/{id:int}", func(ctx context.Context) {
|
||||
id, _ := ctx.Params().GetInt("id") // or .Get to get its string represatantion.
|
||||
username := ctx.PostValue("username")
|
||||
ctx.Writef("update user for id= %d and new username= %s", id, username)
|
||||
})
|
||||
|
||||
// DELETE: http://localhost:8080/users/42
|
||||
usersRoutes.Delete("/{id:int}", func(ctx context.Context) {
|
||||
id, _ := ctx.Params().GetInt("id")
|
||||
ctx.Writef("delete user by id: %d", id)
|
||||
})
|
||||
|
||||
// Subdomains, depends on the host, you have to edit the hosts or nginx/caddy's configuration if you use them.
|
||||
//
|
||||
// See more subdomains examples at _examples/subdomains folder.
|
||||
adminRoutes := app.Party("admin.")
|
||||
|
||||
// GET: http://admin.localhost:8080
|
||||
adminRoutes.Get("/", info)
|
||||
// GET: http://admin.localhost:8080/settings
|
||||
adminRoutes.Get("/settings", info)
|
||||
|
||||
// Wildcard/dynamic subdomain
|
||||
dynamicSubdomainRoutes := app.Party("*.")
|
||||
|
||||
// GET: http://any_thing_here.localhost:8080
|
||||
dynamicSubdomainRoutes.Get("/", info)
|
||||
|
||||
// GET: http://localhost:8080/
|
||||
// GET: http://localhost:8080/profile/anyusername
|
||||
// GET: http://localhost:8080/profile/anyusername/backups/any/number/of/paths/here
|
||||
|
||||
// GET: http://localhost:8080/users/help
|
||||
// GET: http://localhost:8080/users
|
||||
// GET: http://localhost:8080/users/42
|
||||
// POST: http://localhost:8080/users
|
||||
// PUT: http://localhost:8080/users
|
||||
// DELETE: http://localhost:8080/users/42
|
||||
|
||||
// GET: http://admin.localhost:8080
|
||||
// GET: http://admin.localhost:8080/settings
|
||||
// GET: http://any_thing_here.localhost:8080
|
||||
if err := app.Run(iris.Addr(":8080")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func info(ctx context.Context) {
|
||||
method := ctx.Method() // the http method requested a server's resource.
|
||||
subdomain := ctx.Subdomain() // the subdomain, if any.
|
||||
|
||||
// the request path (without scheme and host).
|
||||
path := ctx.Path()
|
||||
// how to get all parameters, if we don't know
|
||||
// the names:
|
||||
paramsLen := ctx.Params().Len()
|
||||
|
||||
ctx.Params().Visit(func(name string, value string) {
|
||||
ctx.Writef("%s = %s\n", name, value)
|
||||
})
|
||||
ctx.Writef("\nInfo\n\n")
|
||||
ctx.Writef("Method: %s\nSubdomain: %s\nPath: %s\nParameters length: %d", method, subdomain, path, paramsLen)
|
||||
}
|
||||
35
_examples/routing/reverse/main.go
Normal file
35
_examples/routing/reverse/main.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/router"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
// need for manually reverse routing when needed outside of view engine.
|
||||
// you normally don't need it because of the {{ urlpath "routename" "path" "values" "here"}}
|
||||
rv := router.NewRoutePathReverser(app)
|
||||
|
||||
myroute := app.Get("/anything/{anythingparameter:path}", func(ctx context.Context) {
|
||||
paramValue := ctx.Params().Get("anythingparameter")
|
||||
ctx.Writef("The path after /anything is: %s", paramValue)
|
||||
})
|
||||
|
||||
// useful for links, although iris' view engine has the {{ urlpath "routename" "path values"}} already.
|
||||
app.Get("/reverse_myroute", func(ctx context.Context) {
|
||||
myrouteRequestPath := rv.Path(myroute.Name, "any/path")
|
||||
ctx.HTML("Should be <b>/anything/any/path</b>: " + myrouteRequestPath)
|
||||
})
|
||||
|
||||
// execute a route, similar to redirect but without redirect :)
|
||||
app.Get("/execute_myroute", func(ctx context.Context) {
|
||||
ctx.Exec("GET", "/anything/any/path") // like it was called by the client.
|
||||
})
|
||||
|
||||
// http://localhost:8080/reverse_myroute
|
||||
// http://localhost:8080/execute_myroute
|
||||
// http://localhost:8080/anything/any/path/here
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
39
_examples/routing/route-state/main.go
Normal file
39
_examples/routing/route-state/main.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
none := app.None("/invisible/{username}", func(ctx context.Context) {
|
||||
ctx.Writef("Hello %s with method: %s", ctx.Values().GetString("username"), ctx.Method())
|
||||
|
||||
if from := ctx.Values().GetString("from"); from != "" {
|
||||
ctx.Writef("\nI see that you're coming from %s", from)
|
||||
}
|
||||
})
|
||||
|
||||
app.Get("/change", func(ctx context.Context) {
|
||||
|
||||
if none.IsOnline() {
|
||||
none.Method = iris.MethodNone
|
||||
} else {
|
||||
none.Method = iris.MethodGet
|
||||
}
|
||||
|
||||
// refresh re-builds the router at serve-time in order to be notified for its new routes.
|
||||
app.RefreshRouter()
|
||||
})
|
||||
|
||||
app.Get("/execute", func(ctx context.Context) {
|
||||
// same as navigating to "http://localhost:8080/invisible/iris" when /change has being invoked and route state changed
|
||||
// from "offline" to "online"
|
||||
ctx.Values().Set("from", "/execute") // values and session can be shared when calling Exec from a "foreign" context.
|
||||
ctx.Exec("GET", "/invisible/iris")
|
||||
})
|
||||
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
Reference in New Issue
Block a user