perwendel / spark

A simple expressive web framework for java. Spark has a kotlin DSL https://github.com/perwendel/spark-kotlin
Apache License 2.0
9.64k stars 1.56k forks source link

Redundant logic in routes #938

Open paulkagiri opened 6 years ago

paulkagiri commented 6 years ago

Suppose you are developing an Api that does some authentication and authorization on incoming requests like:

For the first case, it's easy, just have a before for all urls

The second is not so easy, it would have been easier if Spark supported annotations such that every request specifies whether it needs the client to be authorized and if so, which role(normal/admin).

Spark doesn't support annotations and thus one has to repeat the logic for all urls. One can argue that one can use path to group urls but what if only some urls in a group need authorization.

Any design/architectural ideas are welcome.

tipsy commented 6 years ago

In my apps I usually create a wrapper for get/post/put/etc, so I can declare routes like:

get("/some-route", ((req, res) -> ...), roles(LOGGED_IN));
post("/some-other-route", ((req, res) -> ...), roles(ADMIN));
public Object get(String path, Route route, List<Role> permittedRoles) {
    return Spark.get(path, (req, res) -> accessManager.manage(route, req, res, permittedRoles));
}
@FunctionalInterface
public interface AccessManager {
    void manage(Route route, Request request, Response response, List<Role> permittedRoles) throws Exception;
}
paulkagiri commented 6 years ago

Thanks, let me explore this and see. I am using Kotlin Spark, let me see how it will look like.

nixedmelon commented 6 years ago

I have an abstract class that implements the Route interface:

abstract class RouteHandler implements Route { ... }

I also declare an abstract method:

abstract Object authenticatedRoute(Integer authenticatedUser, Request request, Response response) { ... }

You can annotate the abstract method in subclasses, then use reflection in the handle method to determine what sort of authentication to perform, if any at all. If it fails, you simply return an error, otherwise you would continue to your abstract method that now contains user information, or whatever else you may need.