-
Notifications
You must be signed in to change notification settings - Fork 86
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
Middleware usage #32
Comments
I think it's technically possible by simply return router.all('/<ignored|.*>', (Request request) {
// Do something here....
return null;
});
router.get(....); Maybe we should create a |
router.all('/<ignored|.*>', (Request request) {
// Do something here....
return null;
});
router.get(....); the callback on the router.all is only called before and not after the router.get. Which is not how Middleware work. maybe we can add a named parameter to router.get/all/post/etc called middleware where we can pass a Middleware object. |
maybe something like that ? router.get('/say-hi/<name>', (Request request, String name) {
return Response.ok('hi $name');
}, requestHandler: (req) {
return req.change(context: {'logHandlerTime': Stopwatch()..start()});
}, responseHandler: (res, req) {
final sw = req.context['logHandlerTime'] as Stopwatch;
print(sw.elapsed);
return null;
}); trying some API this example raised a problem which is we can't reuse middleware across different route :/ |
@Kleak, another option is to leave middleware to Looking at: https://pub.dev/documentation/shelf/latest/shelf/Pipeline-class.html Mostly, you'll want middleware to wrap all requests, so one would do something like: final router = Router();
router.get(...); // setup routes + handlers
final pipeline = Pipeline()
.addMiddleware(loggingMiddleware)
.addMiddleware(cachingMiddleware)
.addHandler(router.handler); In the rare case that we need a set of middlewares that wraps all end-points under That way we keep the logic simple, and reuse the constructs given in |
That's seems great, I always forget the Pipeline class 🙁. I can make a PR to add this as an example. |
In fact there is a little problem. |
Yes. I wish we could do some sort of middleware annotations too.. Like: class MyService {
@shelf_router.Route.get('/my-path/<param>')
@WithAuthorization(permission: 'user-read')
shelf.Response myHandler(shelf.Request req, String param) {...}
}
class WithAuthorization extends shelf_router.MiddlewareAnnotation {
/// Required permissions
final String permission;
const WithAuthorization({this.permission})
@override
shelf.Response wrapHandler(shelf.Request req, shelf.Response Function(shelf.Request) handler) {
if (!getUser(req).permissions.contains(this.permission)) {
return shelf.Response.unauthorized();
}
return handler(req);
}
} Such that we can create custom annotations by extending |
If someone wants to draft a design and implement this, I'll be happy to help with reviews. |
@jonasfj In your example the |
@Kleak, yes, that's the thinking.. So the code-generator would be aware of annotations that implement I'm not sure exactly how this would work, or if it's flexible enough.. But playing with this might be a good enough start. |
Seems you added a way to add a middleware but didn’t expose it in shelf_router. Seems the signature of the 2 functions are not compatible we will need to do something between. |
Ah, yes, But I see no reason Like said, this was just a possible design -- I think it can be implemented.
I haven't added anything for middleware, I just outline a possible design. And yes, ideally it would be exposed in I haven't explored this in depth yet, there is lots of details to be worked out. These are just ideas. |
ah, I had completely forgotten about that. Hmm, I don't think I ever really considered how middleware would be added. Using a named parameter for adding middleware is certainly an option. But I'm not sure it's very pretty or ergonomic. Crazy idea: given that middlewares always have the signature: We could make it so that We would need to refactor the internals of Example var app = Router();
// Middleware applied to all requests for all handlers after this point.
app.all('/<|.*>', (Request request, Handler handler) async {
// Tweak [request] as desired
final response = await handler(request);
// Tweak [response] as desired
return response;
});
app.get('/hello', (Request request) {
return Response.ok('hello-world');
});
// Middeware applied to /user/<user>
app.get('/user/<user>', (Request request, Handler handler) async {
// Tweak [request] as desired
final response = await handler(request);
// Tweak [response] as desired
return response;
});
app.get('/user/<user>', (Request request, String user) {
return Response.ok('hello $user');
});
// Note: Declaring middleware here is useless because handlers are declared higher up.
var server = await io.serve(app.handler, 'localhost', 8080); This might be too complicated. Perhaps it's better to have a single |
i think so :/ Thinking about an other approach which is breaking. app.get('/user/<user>', (Request request, String user) {
return Response.ok('hello $user');
}); if this handler doesn't get the url params And to get the url params we can write an extension on Request to get the params from the context (they already are in the context so it would not be difficult). |
a pseudo-code of my example above app.get('/user/<user>', (Request request) {
return Response.ok('hello ${request.getPathParams['user']}');
}); and with middleware : app.get(
'/user/<user>',
Pipeline().addMiddleware(myMiddleware).addHandler((Request request) {
return Response.ok('hello ${request.getPathParams['user']}');
})
); |
app.get(
'/user/<user>',
Pipeline().addMiddleware(myMiddleware).addHandler((Request request) {
return Response.ok('hello ${shelf_router.params(request, 'user')}');
})
); This you can already do using params. The signature of the handled is NOT required to have all URL parameters. It's required to have none OR all. If this isn't evident from examples and documentation feel free to suggest updates. Maybe we should add a section in the |
that's true i did it little bit after writing my message. |
I think using |
Hi, Middlewares should have the capability
Authentication for example: And we have a middleware like: Future<Response> authenticationMiddleware(Request request) async {
// check user token.
final user = {}; // find user from DB or check JWT.
// user not found, we return an object and interrupt the request so the handler does not need to deal with authentication, we can be sure if the request reached the handler then the user has the necessary authentication.
if (user == null) {
return Response.forbidden("You have no permission.");
}
request.context["user"] = user;
next();
} // no need for authentication.
app.get("/login",loginRouteHandler);
// we need an authentication.
app.get("/me", [ authenticationMiddleware ] ,meRouteHandle); Another use case is the body or URL parsing. We can give the name of the model to the middleware that implements Serializable and if the middleware can not parse the body, it interrupts the request and returns a' bad request' error. If the request reaches the handler, the handler just reads the parsed model and uses it. I am not sure how can we give a parameter to the middleware function, but we can even start with creating middleware for everybody object since everybody object is defined as DTO, a separate class that is created for data serving and accepting. I believe both scenarios are a must for enterprise-grade backend applications. In Router class there is some code about middleware, I am not sure why we cannot give a middleware with app.get or app.post and NY other methods. I can write middleware like in the example but:
Here is a trick to give a middleware for a router: app.get("/test", (Request request) => authenticationMiddleware(request, AuthController().me)); I can also try to implement that feature if you may. Thanks. |
Is there a way to use Middleware with this package?
And better to annotate a Route with some middleware we want to use for this route ?
The text was updated successfully, but these errors were encountered: