jooby-project / jooby

The modular web framework for Java and Kotlin
https://jooby.io
Apache License 2.0
1.7k stars 199 forks source link

Is it possible to attach handlers to only specific routes? #3480

Closed nonetallt closed 1 month ago

nonetallt commented 1 month ago

In the documentation, all of the examples I've seen register request handlers using methods such as:

Is there a way to limit the scope of these handlers to only affect some routes instead of every route?

Would it be possible to apply a handler to only the specified controller methods when using the MVC API for example?

EDIT: I noticed that sections 3.4 and 3.5 of the documentation address this subject. However I'm not sure how to apply it to the controller routes in a MVC setup.

agentgt commented 1 month ago

Its based on the order (unless that has changed recently).

That is if you do not want a filter to apply to a controller, install the controller first then install the filter.

jknack commented 1 month ago

Works as @agentgt said. Optionally, you can group handler with filter:

routes(() -> {
    use(myfilter);
    get("/", ctx -> "Hello");

});

get("/route-without-myfilter", ...);

See https://jooby.io/#router-pipeline and also https://jooby.io/#router-grouping-routes

nonetallt commented 1 month ago

Thank you for the clarification.

On a related note, I'm having a problem with the after() handler not executing. Am I misunderstanding something because in the documentation it is written that:

An after handler is always invoked.

However, I can not get the "after" handler to execute with the following code:

    before((ctx) -> System.out.println("app: before"));
    after((ctx, res, fail) -> System.out.println("app: after"));

    get("/test", ctx -> {
      System.out.println("test");
      return "foo";
    });

Only app:before and test are printed in the log when accessing /test.

jknack commented 1 month ago

Hi, this works as expected:


public class App extends Jooby {

  {
    before((ctx) -> System.out.println("app: before"));
    after((ctx, res, fail) -> System.out.println("app: after"));

    get("/test", ctx -> {
      System.out.println("test");
      return "foo";
    });
  }

  public static void main(final String[] args) {
    runApp(args, App::new);
  }

}

Output:

app: before
test
app: after
nonetallt commented 1 month ago

Weird. Any idea what might cause the handler to not execute? I obviously have other stuff as well in the app initialization but no idea why only the after handler seems messed up.

I suppose I have to start commenting out stuff to isolate the problem.

nonetallt commented 1 month ago

It seems like the issue is somehow related to ReactiveSupport.

When this line is enabled before the routes:

use(ReactiveSupport.concurrent());

The after handler will not executed. However when I comment out the line it will work as expected.

jknack commented 1 month ago

yea, try to not mix the reactive/non-blocking with normal routes:

use(ReactiveSupport.concurrent());

    before((ctx) -> System.out.println("app: before"));
    after((ctx, res, fail) -> System.out.println("app: after"));

    get("/test", ctx -> {
      System.out.println("test");
      return CompletableFuture.completedFuture("foo");
    });

So here the after is attached to the CompletableFuture and it is executed.

nonetallt commented 1 month ago

I removed the ReactiveSupport usage.

It seems that the default execution mode should be able to handle non-blocking responses as long as the return value is a CompletableFuture even without calling use(ReactiveSupport.concurrent());.

I'm not sure what exactly is the point of explicitly calling that method. It seems somewhat misleading and also counterintuitive that basically everything has been working seemingly fine with that line included but not the after handler.