Funqy HTTP Binding (Standalone)
The guide walks through quickstart code to show you how you can deploy Funqy as a standalone service and invoke on Funqy functions using HTTP.
The Funqy HTTP binding is not a replacement for REST over HTTP. Because Funqy needs to be portable across a lot of different protocols and function providers its HTTP binding is very minimalistic and you will lose REST features like linking and the ability to leverage HTTP features like cache-control and conditional GETs. You may want to consider using Quarkus’s Jakarta REST, Spring MVC, or Vert.x Web Reactive Routes support instead, although Funqy will have less overhead than these alternatives (except Vert.x which is still super fast). |
准备
要完成本指南,您需要:
-
Roughly 15 minutes
-
An IDE
-
JDK 11+ installed with
JAVA_HOME
configured appropriately -
Apache Maven 3.9.6
-
Optionally the Quarkus CLI if you want to use it
-
Read about Funqy Basics. This is a short read!
The Quickstart
Clone the Git repository: git clone https://github.com/quarkusio/quarkus-quickstarts.git
, or download
an archive.
The solution is located in the funqy-http-quickstart
directory.
The Code
If you look at the Java code, you’ll see that there is no HTTP specific
API. Its just simple Java methods annotated with @Funq
. Simple, easy,
straightforward.
Maven Dependencies
To write Funqy HTTP functions, simply include the quarkus-funqy-http
dependency into your Quarkus pom.xml
file:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-funqy-http</artifactId>
</dependency>
Execute Funqy HTTP functions
The URL path to execute a function is the function name. For example if
your function name is foo
then the URL path to execute the function would
be /foo
.
The HTTP POST or GET methods can be used to invoke on a function. The return value of the function is marshalled to JSON using the Jackson JSON library. Jackson annotations can be used. If your function has an input parameter, a POST invocation must use JSON as the input type. Jackson is also used here for unmarshalling.
You can invoke the hello
function defined in
PrimitiveFunctions.java
by pointing your browser to http://localhost:8080/hello
Invoking the other functions in the quickstart requires an HTTP POST. To
execute the greet
function defined in
GreetingFunction.java
invoke this curl script.
curl "http://localhost:8080/greet" \
-X POST \
-H "Content-Type: application/json" \
-d '{"name":"Bill"}'
Primitive types can also be passed as input using the standard JSON mapping
for them. To execute the toLowerCase
function defined in
PrimitiveFunctions.java
invoke this curl script:
curl "http://localhost:8080/toLowerCase" \
-X POST \
-H "Content-Type: application/json" \
-d '"HELLO WORLD"'
To execute the double
function defined in
PrimitiveFunctions.java
invoke this curl script:
curl "http://localhost:8080/double" \
-X POST \
-H "Content-Type: application/json" \
-d '2'
GET Query Parameter Mapping
For GET requests, the Funqy HTTP Binding also has a query parameter mapping
for function input parameters. Only bean style classes and java.util.Map
can be used for your input parameter. For bean style classes, query
parameter names are mapped to properties on the bean class. Here’s an
example of a simple Map
:
@Funq
public String hello(Map<String, Integer> map) {
...
}
Key values must be a primitive type (except char) or String
. Values can
be primitives (except char), String
, OffsetDateTime
or a complex bean
style class. For the above example, here’s the corresponding curl request:
curl "http://localhost:8080/a=1&b=2"
The map
input parameter of the hello
function would have the key value
pairs: a
→1, b
→2.
Bean style classes can also be use as the input parameter type. Here’s an example:
public class Person {
String first;
String last;
public String getFirst() { return first; }
public void setFirst(String first) { this.first = first; }
public String getLast() { return last; }
public void setLast(String last) { this.last = last; }
}
public class MyFunctions {
@Funq
public String greet(Person p) {
return "Hello " + p.getFirst() + " " + p.getLast();
}
}
Property values can be any primitive type except char
. It can also be
String
, and OffsetDateTime
. OffsetDateTime
query param values must be
in ISO-8601 format.
You can invoke on this using an HTTP GET and query parameters:
curl "http://localhost:8080/greet?first=Bill&last=Burke"
In the above request, the query parameter names are mapped to corresponding properties in the input class.
The input class can also have nested bean classes. Expanding on the previous example:
public class Family {
private Person dad;
private Person mom;
public Person getDad() { return dad; }
public void setDad(Person dad) { this.dad = dad; }
public Person getMom() { return mom; }
public void setMom(Person mom) { this.mom = mom; }
}
public class MyFunctions {
@Funq
public String greet(Family family) {
...
}
}
In this case, query parameters for nested values use the .
notation. For
example:
curl "http://localhost:8080/greet?dad.first=John&dad.last=Smith&mom.first=Martha&mom.last=Smith"
java.util.List
and Set
are also supported as property values. For
example:
public class Family {
...
List<String> pets;
}
public class MyFunctions {
@Funq
public String greet(Family family) {
...
}
}
To invoke a GET request, just list the pets
query parameter multiple
times.
curl "http://localhost:8080/greet?pets=itchy&pets=scratchy"
For more complex types, List
and Set
members must have an identifier in
the query parameter. For example:
public class Family {
...
List<Person> kids;
}
public class MyFunctions {
@Funq
public String greet(Family family) {
...
}
}
Each kids
query parameter must identify the kid they are referencing so
that the runtime can figure out which property values go to which members in
the list. Here’s the curl request:
curl "http://localhost:8080/greet?kids.1.first=Buffy&kids.2.first=Charlie"
The above URL uses the value 1
and 2
to identity the target member of
the list, but any unique string can be used.
A property can also be a java.util.Map
. The key of the map can be any
primitive type and String
. For example:
public class Family {
...
Map<String, String> address;
}
public class MyFunctions {
@Funq
public String greet(Family family) {
...
}
}
The corresponding call would look like this:
curl "http://localhost:8080/greet?address.state=MA&address.city=Boston"
If your Map
value is a complex type, then just continue the notation by
adding the property to set at the end.
public class Family {
...
Map<String, Address> addresses;
}
public class MyFunctions {
@Funq
public String greet(Family family) {
...
}
}
curl "http://localhost:8080/greet?addresses.home.state=MA&addresses.home.city=Boston"