Skip to content

Commit

Permalink
init of v11.2.0: add context#FullRequestURI and NewConditionalHandler
Browse files Browse the repository at this point in the history
As requested at: #1167 and #1170
  • Loading branch information
kataras committed Jan 19, 2019
1 parent f4f6607 commit dee3e6b
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 6 deletions.
49 changes: 49 additions & 0 deletions _examples/routing/conditional-chain/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import (
"github.com/kataras/iris"
)

func main() {
app := iris.New()
v1 := app.Party("/api/v1")

myFilter := func(ctx iris.Context) bool {
// don't do that on production, use session or/and database calls and etc.
ok, _ := ctx.URLParamBool("admin")
return ok
}

onlyWhenFilter1 := func(ctx iris.Context) {
ctx.Application().Logger().Infof("admin: %s", ctx.Params())
ctx.Next()
}

onlyWhenFilter2 := func(ctx iris.Context) {
// You can always use the per-request storage
// to perform actions like this ofc.
//
// this handler: ctx.Values().Set("is_admin", true)
// next handler: isAdmin := ctx.Values().GetBoolDefault("is_admin", false)
//
// but, let's simplify it:
ctx.HTML("<h1>Hello Admin</h1><br>")
ctx.Next()
}

// HERE:
// It can be registered anywhere, as a middleware.
// It will fire the `onlyWhenFilter1` and `onlyWhenFilter2` as middlewares (with ctx.Next())
// if myFilter pass otherwise it will just continue the handler chain with ctx.Next() by ignoring
// the `onlyWhenFilter1` and `onlyWhenFilter2`.
myMiddleware := iris.NewConditionalHandler(myFilter, onlyWhenFilter1, onlyWhenFilter2)

v1UsersRouter := v1.Party("/users", myMiddleware)
v1UsersRouter.Get("/", func(ctx iris.Context) {
ctx.HTML("requested: <b>/api/v1/users</b>")
})

// http://localhost:8080/api/v1/users
// http://localhost:8080/api/v1/users?admin=true
app.Run(iris.Addr(":8080"))
}
30 changes: 25 additions & 5 deletions context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,14 +292,16 @@ type Context interface {
// RequestPath returns the full request path,
// based on the 'escape'.
RequestPath(escape bool) string

// Host returns the host part of the current url.
Host() string
// Subdomain returns the subdomain of this request, if any.
// Note that this is a fast method which does not cover all cases.
Subdomain() (subdomain string)
// IsWWW returns true if the current subdomain (if any) is www.
IsWWW() bool
// FullRqeuestURI returns the full URI,
// including the scheme, the host and the relative requested path/resource.
FullRequestURI() string
// RemoteAddr tries to parse and return the real client's request IP.
//
// Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders.
Expand Down Expand Up @@ -1465,11 +1467,11 @@ func (ctx *context) Host() string {

// GetHost returns the host part of the current URI.
func GetHost(r *http.Request) string {
h := r.URL.Host
if h == "" {
h = r.Host
if host := r.Host; host != "" {
return host
}
return h

return r.URL.Host
}

// Subdomain returns the subdomain of this request, if any.
Expand Down Expand Up @@ -1502,6 +1504,24 @@ func (ctx *context) IsWWW() bool {
return false
}

// FullRqeuestURI returns the full URI,
// including the scheme, the host and the relative requested path/resource.
func (ctx *context) FullRequestURI() string {
scheme := ctx.request.URL.Scheme
if scheme == "" {
if ctx.request.TLS != nil {
scheme = "https:"
} else {
scheme = "http:"
}
}

host := ctx.Host()
path := ctx.Path()

return scheme + "//" + host + path
}

const xForwardedForHeaderKey = "X-Forwarded-For"

// RemoteAddr tries to parse and return the real client's request IP.
Expand Down
52 changes: 52 additions & 0 deletions context/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,55 @@ func HandlerName(h Handler) string {
// return fmt.Sprintf("%s:%d", l, n)
return runtime.FuncForPC(pc).Name()
}

// Filter is just a type of func(Handler) bool which reports whether an action must be performed
// based on the incoming request.
//
// See `NewConditionalHandler` for more.
type Filter func(Context) bool

// NewConditionalHandler returns a single Handler which can be registered
// as a middleware.
// Filter is just a type of Handler which returns a boolean.
// Handlers here should act like middleware, they should contain `ctx.Next` to proceed
// to the next handler of the chain. Those "handlers" are registed to the per-request context.
//
//
// It checks the "filter" and if passed then
// it, correctly, executes the "handlers".
//
// If passed, this function makes sure that the Context's information
// about its per-request handler chain based on the new "handlers" is always updated.
//
// If not passed, then simply the Next handler(if any) is executed and "handlers" are ignored.
//
// Example can be found at: _examples/routing/conditional-chain.
func NewConditionalHandler(filter Filter, handlers ...Handler) Handler {
return func(ctx Context) {
if filter(ctx) {
// Note that we don't want just to fire the incoming handlers, we must make sure
// that it won't break any further handler chain
// information that may be required for the next handlers.
//
// The below code makes sure that this conditional handler does not break
// the ability that iris provides to its end-devs
// to check and modify the per-request handlers chain at runtime.
currIdx := ctx.HandlerIndex(-1)
currHandlers := ctx.Handlers()
if currIdx == len(currHandlers)-1 {
// if this is the last handler of the chain
// just add to the last the new handlers and call Next to fire those.
ctx.AddHandler(handlers...)
ctx.Next()
return
}
// otherwise insert the new handlers in the middle of the current executed chain and the next chain.
newHandlers := append(currHandlers[:currIdx], append(handlers, currHandlers[currIdx+1:]...)...)
ctx.SetHandlers(newHandlers)
ctx.Next()
return
}
// if not pass, then just execute the next.
ctx.Next()
}
}
6 changes: 6 additions & 0 deletions go19.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ type (
// If Handler panics, the server (the caller of Handler) assumes that the effect of the panic was isolated to the active request.
// It recovers the panic, logs a stack trace to the server error log, and hangs up the connection.
Handler = context.Handler
// Filter is just a type of func(Handler) bool which reports whether an action must be performed
// based on the incoming request.
//
// See `NewConditionalHandler` for more.
// An alias for the `context/Filter`.
Filter = context.Filter
// A Map is a shortcut of the map[string]interface{}.
Map = context.Map

Expand Down
3 changes: 2 additions & 1 deletion httptest/httptest.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"net/http"
"testing"

"github.com/iris-contrib/httpexpect"
"github.com/kataras/iris"

"github.com/iris-contrib/httpexpect"
)

type (
Expand Down
18 changes: 18 additions & 0 deletions iris.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,24 @@ var (
//
// A shortcut for the `context#LimitRequestBodySize`.
LimitRequestBodySize = context.LimitRequestBodySize
// NewConditionalHandler returns a single Handler which can be registered
// as a middleware.
// Filter is just a type of Handler which returns a boolean.
// Handlers here should act like middleware, they should contain `ctx.Next` to proceed
// to the next handler of the chain. Those "handlers" are registed to the per-request context.
//
//
// It checks the "filter" and if passed then
// it, correctly, executes the "handlers".
//
// If passed, this function makes sure that the Context's information
// about its per-request handler chain based on the new "handlers" is always updated.
//
// If not passed, then simply the Next handler(if any) is executed and "handlers" are ignored.
// Example can be found at: _examples/routing/conditional-chain.
//
// A shortcut for the `context#NewConditionalHandler`.
NewConditionalHandler = context.NewConditionalHandler
// StaticEmbeddedHandler returns a Handler which can serve
// embedded into executable files.
//
Expand Down

0 comments on commit dee3e6b

Please sign in to comment.