Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions vertx-web/src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,28 @@ There are a couple of rules that must be fulfilled before sub routers can be use
Validation happens at the time the router is added to the http server. This means that you cannot get any validation
error during the build time because of the dynamic nature of sub routers. They depend on the context to be validated.

== Branching and routers

It is sometimes useful to construct routing handlers as trees rather than chains.

The `BranchingHandler` interface provides handlers that can be used to implement simple conditional branching, as in:

[source,java]
----
{@link examples.WebExamples#example90}
----

In the example above, a predicate checks if a `Foo` header is present in the HTTP request, then forwards the processing
to the correct handler.

This helper is mostly useful when you need to create non-trivial routing constructs in a programmatic fashion.
A good example is supporting _route predicates_ as in the following example:

[source,java]
----
{@link examples.WebExamples#example91}
----

== Localization

Vert.x Web parses the `Accept-Language` header and provides some helper methods to identify which is the preferred
Expand Down
35 changes: 35 additions & 0 deletions vertx-web/src/main/java/examples/WebExamples.java
Original file line number Diff line number Diff line change
Expand Up @@ -2051,4 +2051,39 @@ public void example89(Vertx vertx, Router router, CredentialStorage credentialSt
// secure the remaining routes
router.route().handler(webAuthNHandler);
}

public void example90(Vertx vertx, Router router) {
router.get("/branching").handler(BranchingHandler.create(
rc -> rc.request().headers().contains("Foo"),
rc -> {
String foo = rc.request().getHeader("Foo");
rc.response().end("Foo: " + foo);
},
rc -> rc.response().end("N/A")
));
}

public void example91(Vertx vertx, Router router) {
// Special case if 'X-REQUEST-CLIENT-TYPE' is present, else forward to the next handler
router.get("/profile").handler(BranchingHandler.create(
rc -> "mobile".equals(rc.request().getHeader("X-REQUEST-CLIENT-TYPE")),
this::handleMobileProfileRequest,
RoutingContext::next
));

// Handle the general case, or fail with a 401 is the 'Authorization` header is missing
router.get("/profile").handler(BranchingHandler.create(
rc -> rc.request().headers().contains("Authorization"),
this::handleProfileRequest,
rc -> rc.fail(401)
));
}

private void handleMobileProfileRequest(RoutingContext rc) {
// Nothing
}

private void handleProfileRequest(RoutingContext rc) {
// Nothing
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.vertx.ext.web.handler;

import io.vertx.codegen.annotations.GenIgnore;
import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.impl.BranchingHandlerImpl;

import java.util.function.Predicate;

/**
* A handler that performs conditional branching based on a {@link RoutingContext} {@link Predicate}.
*/
@VertxGen
public interface BranchingHandler extends Handler<RoutingContext> {

/**
* Creates a branching handler that performs conditional branching.
*
* @param test the predicate to evaluate the {@link RoutingContext}
* @param trueBranch the handler to execute if the predicate evaluates to {@code true}
* @param falseBranch the handler to execute if the predicate evaluates to {@code false}
* @return a new instance of {@link BranchingHandler} configured with the specified predicate and handlers
*/
@GenIgnore(GenIgnore.PERMITTED_TYPE)
static BranchingHandler create(Predicate<RoutingContext> test, Handler<RoutingContext> trueBranch, Handler<RoutingContext> falseBranch) {
return new BranchingHandlerImpl(test, trueBranch, falseBranch);
}

/**
* Creates a branching handler that performs conditional branching where the {@code false} branch is a call to
* {@link RoutingContext#next()}.
*
* This is useful to define a route predicate, but let further {@link io.vertx.ext.web.Router} handlers take over the
* handling duties.
*
* @param test the predicate to evaluate the {@link RoutingContext}
* @param trueBranch the handler to execute if the predicate evaluates to {@code true}
* @return a new instance of {@link BranchingHandler} configured with the specified predicate and handler
*/
@GenIgnore(GenIgnore.PERMITTED_TYPE)
static BranchingHandler create(Predicate<RoutingContext> test, Handler<RoutingContext> trueBranch) {
return new BranchingHandlerImpl(test, trueBranch);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.vertx.ext.web.handler.impl;

import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BranchingHandler;

import java.util.function.Predicate;

public class BranchingHandlerImpl implements BranchingHandler {

private final Predicate<RoutingContext> test;
private final Handler<RoutingContext> trueBranch;
private final Handler<RoutingContext> falseBranch;

public BranchingHandlerImpl(Predicate<RoutingContext> test, Handler<RoutingContext> trueBranch, Handler<RoutingContext> falseBranch) {
this.test = test;
this.trueBranch = trueBranch;
this.falseBranch = falseBranch;
}

public BranchingHandlerImpl(Predicate<RoutingContext> test, Handler<RoutingContext> trueBranch) {
this(test, trueBranch, RoutingContext::next);
}

@Override
public void handle(RoutingContext event) {
if (test.test(event)) {
trueBranch.handle(event);
} else {
falseBranch.handle(event);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.vertx.ext.web.tests.handler;

import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.handler.BranchingHandler;
import io.vertx.ext.web.tests.WebTestBase;
import org.junit.Test;

public class BranchingHandlerTest extends WebTestBase {

@Test
public void testHeaderBasedBranching() throws Exception {
router.get("/branching").handler(BranchingHandler.create(
rc -> rc.request().getHeader("Foo") != null,
rc -> rc.response().end("true"),
rc -> rc.response().end("false")
));
testRequest(HttpMethod.GET, "/branching", 200, "OK", "false");
testRequest(HttpMethod.GET, "/branching", request -> {
request.putHeader("Foo", "123");
}, 200, "OK", "true");
}

@Test
public void testHeaderBasedBranchingToNextHandlers() throws Exception {
router.get("/branching").handler(BranchingHandler.create(
rc -> rc.request().getHeader("Foo") != null,
rc -> rc.response().end("handler #1")
));
router.get("/branching").handler(rc -> rc.response().end("handler #2"));
testRequest(HttpMethod.GET, "/branching", 200, "OK", "handler #2");
testRequest(HttpMethod.GET, "/branching", request -> {
request.putHeader("Foo", "123");
}, 200, "OK", "handler #1");
}
}
Loading