Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Latest commit

 

History

History
194 lines (140 loc) · 3.76 KB

File metadata and controls

194 lines (140 loc) · 3.76 KB

properties

Routes have a few properties that let you extend basic functionality in one way or another including:

  • attributes
  • with, map and excludes operators
  • consumes/produces types

attributes

Attributes let you annotate a route at application bootstrap time. It functions like static metadata available at runtime:

{
  get("/path", ..)
    .attr("foo", "bar");
}

An attribute consist of a name and value. Allowed values are primitives, String, enum, class or an array of these types.

Attributes can be accessed at runtime in a request/response cycle. For example, a security module might check for a role attribute, a sitemap generator might check for a priority attribute, etc.

{
  use((req, rsp, chain) -> {
    User user = ...;
    String role = req.route().attr("role");
    if (user.hasRole(role)) {
      chain.next(req, rsp);
    }
    throw new Err(403);
  });
}

In MVC routes you can set attributes for all the web methods:

{
   use(Controller.class)
     .attr("foo", "bar");
}

Or via annotations:

@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public static @interface Role {
  String value();
}

@Path("/path")
public class AdminResource {

  @Role("admin")
  public Object doSomething() {
    ...
  }

}

{
  use("*", (req, rsp) -> {
    System.out.println(req.route().attributes())
  });
}

The previous example will print: {role = admin}.

Any runtime annotation is automatically added as route attributes following these rules:

  • If the annotation has a value method, then we use the annotation's name as the attribute name.
  • Otherwise, we use the method name as the attribute name.

request attributes vs route attributes:

Route attributes are created at bootstrap. They are global, and once set, they won't change.

On the other hand, request attributes are created in a request/response cycle.

with operator

The {{route_with}} operator sets attributes, consumes/produces types, exclusions, etc. to one or more routes:

{
  with(() -> {

    get("/admin/1", ...);

    get("/admin/2", ...);

  }).attr("role", "admin");
}

map operator

The {{route_map}} operator converts a route output to something else:

{
  // we got bar.. not foo
  get("/foo", () -> "foo")
    .map(value -> "bar");

  // we got foo.. not bar
  get("/bar", () -> "bar")
    .map(value -> "foo");
}

If you want to apply a single {{route_map}} to several routes:

{
  with(() -> {
    get("/foo", () -> "foo");

    get("/bar", () -> "bar");

  }).map(v -> "foo or bar");
}

You can apply a Mapper to specific types:

{
  with(() -> {
    get("/str", () -> "str");

    get("/int", () -> 1);

  }).map(String v -> "{" + v + "}");
}

A call to /str produces {str}, while /int just 1.

NOTE: You can apply the map operator to routes that produces an output (a.k.a function routes).

For example, the {{route_map}} operator will be silently ignored here:

{
  get("/", (req, rsp) -> {
    rsp.send(...);
  });
}

excludes

The {{route_excludes}} operator ignores what would otherwise have been a route path match:

{
   use("*", (req, rsp) -> {
     // all except /login
   }).excludes("/login");
}

consumes

The {{route_consumes}} operator indicates the type of input the route can handle.

{
  post("/", req -> {
    MyObject json = req.body().to(MyObject.class);

  }).consumes("json");
}

produces

The {{route_produces}} operator indicates the type of output the route can produce.

{
  get("/", req -> {
    return new MyObject();
  }).produces("json");
}