Skip to content

Latest commit

 

History

History
433 lines (338 loc) · 9.19 KB

File metadata and controls

433 lines (338 loc) · 9.19 KB

Value API

The javadoc:Value[Value] is an unified and type-safe API across all parameter types:

For learning purpose we are going to show all the javadoc:Value[Value] features using query parameters, but keep in mind these features apply to all the parameter types.

Single value

Single value is available via value() or [type]Value() functions:

Java
{
  get("/", ctx -> {
    String name = ctx.query("name").value();                          // (1)

    float score = ctx.query("score").floatValue();                    // (2)

    boolean enabled = ctx.query("enabled").booleanValue();            // (3)

    BigDecimal decimal = ctx.query("decimal").value(BigDecimal::new); // (4)
    ...
  });
}
Kotlin
{
  get("/") {
    val name = ctx.query("name").value()                     // (1)

    val score = ctx.query("score").floatValue()              // (2)

    val enabled = ctx.query("enabled").booleanValue()        // (3)

    val decimal = ctx.query("decimal").value(::BigDecimal)   // (4)
    ...
  });
}

The value() family methods always retrieve a value. If there is no value, a BadRequest(400) response is generated. So single value parameters are required:

  1. Access to query parameter q and convert to String:

    • /?name=foofoo

    • /Bad Request(400): Missing value: "q"

  2. Access to query parameter score and convert to float:

    • /?score=11.0

    • /?score=stringBad Request(400) (Type mismatch: cannot convert to number)

    • /Bad Request(400) (Required parameter score is not present)

  3. Access to query parameter enabled and convert to boolean:

    • /?enabled=truetrue

    • /?enabled=stringBad Request(400) (Type mismatch: cannot convert to boolean)

    • /Bad Request(400): Missing value: "enabled"

  4. Access to query parameter decimal and convert to BigDecimal:

    • /?decimal=2.32.3

    • /?decimal=stringBad Request(400) (Type mismatch: cannot convert to BigDecimal)

    • /Bad Request(400): Missing value: "decimal"

Default and Optional value

Default and optional value are available in two different ways:

  • Providing a default value

  • Requesting an java.util.Optional object

Java
{
  get("/search", ctx -> {
    String q = ctx.query("q").value("*:*");             // (1)
    return q;
  });

  get("/search", ctx -> {
    Optional<String> q = ctx.query("q").toOptional();   // (2)
    return q;
  });
}
Kotlin
{
  get("/search") {
    val q = ctx.query("q").value("*:*")    // (1)
    q
  });

  get("/search") {
    val q = ctx.query("q").toOptional();   // (2)
    q
  });
}
  1. Access to query variable q and convert to String with a default value of :.

    • /search?q=foofoo

    • /search:

  2. Access to query variable q and convert to Optional<String>:

    • /search?q=fooOptional[foo]

    • /searchOptional.empty

Multiple values

Multiple values are available via functions:

  • javadoc:Value[toList]: Returns a java.util.List of values

  • javadoc:Value[toSet]: Returns a java.util.Set of values

Java
{
  get("/", ctx -> {
    List<String> q = ctx.query("q").toList();                            // (1)

    List<Integer> n = ctx.query("n").toList(Integer.class);              // (2)

    List<BigDecimal> decimals = ctx.query("d").toList(BigDecimal::new);  // (3)

    ...
  });
}
Kotlin
{
  get("/") {
    val q = ctx.query("q").toList()                     // (1)

    val n = ctx.query("n").toList(Integer.class)        // (2)

    val decimals = ctx.query("d").toList(::BigDecimal)  // (3)

    ...
  });
}
  1. Multi-value query parameter q as List<String>:

    • /[] (empty list)

    • /?q=foo[foo]

    • /?q=foo&q=bar[foo, bar]

  2. Multi-value query parameter as List<Integer>

    • /[] (empty list)

    • /?n=1[1]

    • /?n=1&n=2[1, 2]

  3. Multi-value query parameter as List<BigDecimal>

    • /[] (empty list)

    • /?d=1[1]

    • /?d=1&n=2[1, 2]

Structured data

The javadoc:Value[Value API] provides a way to traverse and parse structured data:

/?user.name=root&user.pass=pass
Traversal
{
  get("/", ctx -> {
    Value user = ctx.query("user");                  // (1)
    String name  = user.get("name").value();         // (2)
    String pass  = user.get("pass").value();         // (3)
    String email = user.get("email").value("none");  // (4)
    ...
  }}
}
Kotlin
{
  get("/") {
    val user = ctx.query("user")              // (1)
    val name  = user["name"].value()          // (2)
    val pass  = user["pass"].value()          // (3)
    val email = user["email"].value("none")   // (4)
    ...
  }}
}
  1. Get the user node

  2. Get the name value from user node

  3. Get the pass value from user node

  4. Get the email value from user node. This is an optional value.

The javadoc:Value[get, java.lang.String] takes a path and returns another value. The returning value may or may not exists.

Syntax

Structured data decoder supports dot and bracket notation:

Dot notation
?member.firstname=Pedro&member.lastname=Picapiedra
Bracket object notation
?member[firstname]=Pedro&member[lastname]=Picapiedra
Bracket array notation for tabular data
?members[0]firstname=Pedro&members[0]lastname=Picapiedra
POJO

Structured data decoder is able to reconstruct a POJO (Plain Old Java Object) from:

We are going to use a Group and Member objects to demonstrate how the decoder works:

Example
class Member {
  public final String firstname;
  public final String lastName;

  public Member(String firstname, String lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
  }
}

class Group {
  public final String id;
  public final List<Member> members;

  public Member(String id, List<Member> members) {
    this.id = id;
    this.members = members;
  }
}
Kotlin
class Member (val firstname: String, lastName: String)

class Group (val id: String, val members: List<Member>)
Member parsing example:
/?firstname=Pedro&lastName=Picapiedra
Java
{
  get("/", ctx -> {
    Member member = ctx.query(Member.class);
    ...
  });
}
Kotlin
{
  get("/") {
    val member = ctx.query<Member>()
    ...
  }
}
Member parsing example from base node:
/?member.firstname=Pedro&member.lastName=Picapiedra
Java
{
  get("/", ctx -> {
    Member member = ctx.query("member").to(Member.class);
    ...
  });
}
Kotlin
{
  get("/") {
    val member = ctx.query("member").to<Member>()
    ...
  });
}

Tabular data uses the bracket array notation:

Member as tabular data:
/?[0]firstname=Pedro&[0]lastName=Picapiedra&[1]firstname=Pablo&[2]lastname=Marmol
Java
{
  get("/", ctx -> {
    List<Member> members = ctx.query().toList(Member.class);
    ...
  });
}
Kotlin
{
  get("/") {
    val members = ctx.query<List<Member>>()
    ...
  });
}
Group with members as tabular data:
/?id=flintstones&members[0]firstname=Pedro&members[0]lastName=Picapiedra
Java
{
  get("/", ctx -> {
    Group group = ctx.query(Group.class);
    ...
  });
}
Kotlin
{
  get("/") {
    val group = ctx.query<Group>()
    ...
  });
}

The target POJO must follow one of these rules:

  • Has a zero argguments/default constructor, or

  • Has only one constructor

  • Has multiple constructors, but only one is annotated with Inject

The decoder matches HTTP parameters in the following order:

  • As constructor arguments

  • As setter method

HTTP parameter name which are not a valid Java identifier must be annotated with Named:

Java
class Member {
  public final String firstname;

  public final String lastname;

  public Member(@Named("first-name") String firstname, @Named("last-name") String lastname) {
    ....
  }
}
Kotlin
class Member (@Named("first-name") val firstname: String, @Named("last-name") val lastName: String)

{love}{love}