-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Add applying rules on middleware #1170
Comments
A middleware is just a function, if you want to call it you can refer to its variable and call it as you do on the first example: You think it all wrong my friend, do not depend on the handler name to decide if you want to execute a middleware or not, design your API smarter, iris helps you on this a lot, make checks based on the Also I don't understand the "apply middleware" in the title, to apply/execute it you just have to call it, nothing crazy, you pass the current context, iris takes cover of everything else. You can even add a handler for a specific request in runtime or do something like // Do calls the SetHandlers(handlers)
// and executes the first handler,
// handlers should not be empty.
//
// It's used by the router, developers may use that
// to replace and execute handlers immediately.
Do(Handlers)
// AddHandler can add handler(s)
// to the current request in serve-time,
// these handlers are not persistenced to the router.
//
// Router is calling this function to add the route's handler.
// If AddHandler called then the handlers will be inserted
// to the end of the already-defined route's handler.
//
AddHandler(...Handler)
// SetHandlers replaces all handlers with the new.
SetHandlers(Handlers)
// Handlers keeps tracking of the current handlers.
Handlers() Handlers
// HandlerIndex sets the current index of the
// current context's handlers chain.
// If -1 passed then it just returns the
// current handler index without change the current index.
//
// Look Handlers(), Next() and StopExecution() too.
HandlerIndex(n int) (currentIndex int)
// Proceed is an alternative way to check if a particular handler
// has been executed and called the `ctx.Next` function inside it.
// This is useful only when you run a handler inside
// another handler. It justs checks for before index and the after index.
//
// A usecase example is when you want to execute a middleware
// inside controller's `BeginRequest` that calls the `ctx.Next` inside it.
// The Controller looks the whole flow (BeginRequest, method handler, EndRequest)
// as one handler, so `ctx.Next` will not be reflected to the method handler
// if called from the `BeginRequest`.
//
// Although `BeginRequest` should NOT be used to call other handlers,
// the `BeginRequest` has been introduced to be able to set
// common data to all method handlers before their execution.
// Controllers can accept middleware(s) from the MVC's Application's Router as normally.
//
// That said let's see an example of `ctx.Proceed`:
//
// var authMiddleware = basicauth.New(basicauth.Config{
// Users: map[string]string{
// "admin": "password",
// },
// })
//
// func (c *UsersController) BeginRequest(ctx iris.Context) {
// if !ctx.Proceed(authMiddleware) {
// ctx.StopExecution()
// }
// }
// This Get() will be executed in the same handler as `BeginRequest`,
// internally controller checks for `ctx.StopExecution`.
// So it will not be fired if BeginRequest called the `StopExecution`.
// func(c *UsersController) Get() []models.User {
// return c.Service.GetAll()
//}
// Alternative way is `!ctx.IsStopped()` if middleware make use of the `ctx.StopExecution()` on failure.
Proceed(Handler) bool
You can also change how middlewares are executed, you can omit the Show me more so I can help you, what conditions do you have for executing a middleware or not in that case? |
My controller implemented |
I know the current implementation of my snippet is not very smart, so I request adding some configuration on applying middlewares on conditions. Allow us to specify the method, route, header, params, query and other conditions to enable middleware. Here is a link as for reference: https://docs.nestjs.com/middleware import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middlewares/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class ApplicationModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
)
.forRoutes(CatsController);
}
} BTW, I'm using iris MVC, seems I can't add middlewares to a specific request handler once I passed the entire route party to a controller? Does I have only one way to use a middleware that |
@zheeeng this doesn't work for you?:
No, mvc depends on a app := iris.New()
// @zheeeng I want the middleware only be applied to the POST, PUT and DELETE
// methods on user route...
userRouter := app.Party("/users", func(ctx iris.Context) {
if method:= ctx.Method(); method == iris.MethodPost || method == iris.MethodPut || method == iris.MethodDelete {
ctx.Next()
return
}
}))
mvc.New(userRouter).Handle(new(UserController))
app.Run(iris.Addr(":8080")) MVC + Middleware examples that can definitely complete all your needs: https://github.com/kataras/iris/blob/master/_examples/mvc/middleware/per-method/main.go#L5 |
Made a high order func helper package irisutils
import "github.com/kataras/iris"
func CreateConditionalMiddleware(
middleware func(ctx iris.Context),
condition func(ctx iris.Context) bool,
) func(ctx iris.Context) {
return func(ctx iris.Context) {
pass := condition(ctx)
if pass {
middleware(ctx)
} else {
ctx.Next()
}
}
} |
OK I will name this 'Filter': func(Context) bool, sounds good? |
I'm not good at naming. |
OK, this is not as simple as the example you gave because in Iris you can modify the handlers per-request and take information about the current chain state at runtime as well (see // 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(iris.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 registered 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.
func NewConditionalHandler(filter Filter, handlers ...iris.Handler) Handler {
return func(ctx iris.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()
}
} And an example of this simple thing: package main
import (
"github.com/kataras/iris"
)
func main() {
app := iris.New()
v1 := app.Party("/api/v1")
myFilter := func(ctx iris.Context) bool {
// ofc... 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 := 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"))
} Thanks for the feature request @zheeeng! |
As requested at: kataras#1167 and kataras#1170 Former-commit-id: 781c92f444b3e362011be886b32cf88f89998589
Can we have a simple way to use a middleware on a specific handler? I made a custom middleware but only want to apply it on
PostUser
handler. Hope there is an approach/ built-in rules configuration to simplify codes below:The text was updated successfully, but these errors were encountered: