Skip to content

Commit

Permalink
Reactive routes - add @param, @Header and @Body
Browse files Browse the repository at this point in the history
- refactor param injectors
- resolves #10596
  • Loading branch information
mkouba committed Jul 10, 2020
1 parent b043880 commit 854b8a0
Show file tree
Hide file tree
Showing 11 changed files with 741 additions and 126 deletions.
60 changes: 51 additions & 9 deletions docs/src/main/asciidoc/reactive-routes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,13 @@ public class SimpleRoutes {
<2> The value of `produces()` is used for content-based routing for all routes where `Route#produces()` is empty.


=== Reactive Route Methods
== Reactive Route Methods

A route method must be a non-private non-static method of a CDI bean.
The annotated method can accept arguments of the following types:
If the annotated method returns `void` then it has to accept at least one argument - see the supported types below.
If the annotated method does not return `void` then the arguments are optional.

A route method can accept arguments of the following types:

* `io.vertx.ext.web.RoutingContext`
* `io.vertx.reactivex.ext.web.RoutingContext`
Expand All @@ -193,10 +196,49 @@ The annotated method can accept arguments of the following types:
* `io.vertx.reactivex.core.http.HttpServerRequest`
* `io.vertx.reactivex.core.http.HttpServerResponse`

If the annotated method returns `void` then it has to accept at least one argument.
If the annotated method does not return `void` then the arguments are optional.
Furhtermore, it is possible to inject the `HttpServerRequest` parameters into a method parameter with type `java.lang.String`/`java.util.Optional<String>` and annotated with `@io.quarkus.vertx.web.Param`:

[source,java]
----
@Route
String hello(@Param Optional<String> name) {
return "Hello " + name.orElse("world");
}
----

The `HttpServerRequest` headers can be injected into a method parameter with type `java.lang.String`/`java.util.Optional<String>` and annotated with `@io.quarkus.vertx.web.Header`:

[source,java]
----
@Route
String helloFromHeader(@Header("My-Header") String header) {
return header;
}
----

The request body can be injected into a method parameter annotated with `@io.quarkus.vertx.web.Body`.

[options="header",cols="1,1"]
|===
|Parameter Type | Obtained via
//-------------
|`java.lang.String` |`routingContext.getBodyAsString()`
|`io.vertx.core.buffer.Buffer` |`routingContext.getBody()`
|`io.vertx.core.json.JsonObject` |`routingContext.getBodyAsJson()`
|`io.vertx.core.json.JsonArray` |`routingContext.getBodyAsJsonArray()`
|any other type |`routingContext.getBodyAsJson().mapTo(MyPojo.class)`
|===
[source,java]
----
@Route(produces = "application/json")
Person createPerson(@Body Person person, @Param("id") Optional<String> primaryKey) {
person.setId(primaryKey.map(Integer::valueOf).orElse(42));
return person;
}
----
==== Returning Unis
=== Returning Unis
In a reactive route, you can return a `Uni` directly:
Expand Down Expand Up @@ -234,7 +276,7 @@ If the returned `Uni` produces a failure (or is `null`), an HTTP 500 response is
Returning a `Uni<Void>` produces a 204 response (no content).
==== Returning results
=== Returning results
You can also return a result directly:
Expand All @@ -256,7 +298,7 @@ The method can return:
* an object - written into the HTTP response after having been encoded into JSON.
The `content-type` header is set to `application/json` if not already set.
==== Returning Multis
=== Returning Multis
A reactive route can return a `Multi`.
The items are written one by one, in the response.
Expand Down Expand Up @@ -298,7 +340,7 @@ Ths previous snippet produces:
{"name":"spiderman", "id": 3} // chunk 3
----
==== Streaming JSON Array items
=== Streaming JSON Array items
You can return a `Multi` to produce a JSON Array, where every item is an item from this array.
The response is written item by item to the client.
Expand Down Expand Up @@ -333,7 +375,7 @@ Using a `Multi<Void>` produces an empty array.
You cannot use `Multi<Buffer>`.
If you need to use `Buffer`, transform the content into a JSON or String representation first.
==== Event Stream and Server-Sent Event support
=== Event Stream and Server-Sent Event support
You can return a `Multi` to produce an event source (stream of server sent events).
To enable this feature, you need to wrap the returned `Multi` using `io.quarkus.vertx.web.ReactiveRoutes.asEventStream`:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
*
* @see AnnotationsTransformer
*/
public final class TransformedAnnotationsBuildItem extends SimpleBuildItem {
public final class TransformedAnnotationsBuildItem extends SimpleBuildItem
implements Function<AnnotationTarget, Collection<AnnotationInstance>> {

private final Function<AnnotationTarget, Collection<AnnotationInstance>> fun;

Expand All @@ -27,4 +28,9 @@ public Collection<AnnotationInstance> getAnnotations(AnnotationTarget target) {
return fun.apply(target);
}

@Override
public Collection<AnnotationInstance> apply(AnnotationTarget target) {
return fun.apply(target);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.quarkus.vertx.web.deployment;

import org.jboss.jandex.DotName;

import io.quarkus.vertx.web.Body;
import io.quarkus.vertx.web.Header;
import io.quarkus.vertx.web.Param;
import io.quarkus.vertx.web.Route;
import io.quarkus.vertx.web.RouteBase;
import io.quarkus.vertx.web.RouteFilter;
import io.quarkus.vertx.web.RoutingExchange;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;

final class DotNames {

static final DotName UNI = DotName.createSimple(Uni.class.getName());
static final DotName MULTI = DotName.createSimple(Multi.class.getName());
static final DotName BUFFER = DotName.createSimple(Buffer.class.getName());
static final DotName RX_HTTP_SERVER_RESPONSE = DotName
.createSimple(io.vertx.reactivex.core.http.HttpServerResponse.class.getName());
static final DotName RX_HTTP_SERVER_REQUEST = DotName
.createSimple(io.vertx.reactivex.core.http.HttpServerRequest.class.getName());
static final DotName HTTP_SERVER_RESPONSE = DotName.createSimple(HttpServerResponse.class.getName());
static final DotName HTTP_SERVER_REQUEST = DotName.createSimple(HttpServerRequest.class.getName());
static final DotName ROUTING_EXCHANGE = DotName.createSimple(RoutingExchange.class.getName());
static final DotName RX_ROUTING_CONTEXT = DotName
.createSimple(io.vertx.reactivex.ext.web.RoutingContext.class.getName());
static final DotName ROUTING_CONTEXT = DotName.createSimple(RoutingContext.class.getName());
static final DotName BODY = DotName.createSimple(Body.class.getName());
static final DotName HEADER = DotName.createSimple(Header.class.getName());
static final DotName PARAM = DotName.createSimple(Param.class.getName());
static final DotName ROUTE_BASE = DotName.createSimple(RouteBase.class.getName());
static final DotName ROUTE_FILTER = DotName.createSimple(RouteFilter.class.getName());
static final DotName ROUTES = DotName.createSimple(Route.Routes.class.getName());
static final DotName ROUTE = DotName.createSimple(Route.class.getName());
static final DotName JSON_OBJECT = DotName.createSimple(JsonObject.class.getName());
static final DotName JSON_ARRAY = DotName.createSimple(JsonArray.class.getName());

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.Type;

import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.vertx.core.buffer.Buffer;

/**
* Describe a request handler.
*/
public class HandlerDescriptor {

private static final DotName DOT_NAME_UNI = DotName.createSimple(Uni.class.getName());
private static final DotName DOT_NAME_MULTI = DotName.createSimple(Multi.class.getName());
private final MethodInfo method;

public HandlerDescriptor(MethodInfo method) {
Expand All @@ -30,11 +26,11 @@ public boolean isReturningVoid() {
}

public boolean isReturningUni() {
return method.returnType().name().equals(DOT_NAME_UNI);
return method.returnType().name().equals(DotNames.UNI);
}

public boolean isReturningMulti() {
return method.returnType().name().equals(DOT_NAME_MULTI);
return method.returnType().name().equals(DotNames.MULTI);
}

public Type getContentType() {
Expand All @@ -55,15 +51,15 @@ public boolean isContentTypeString() {
if (type == null) {
return false;
}
return type.name().equals(DotName.createSimple(String.class.getName()));
return type.name().equals(io.quarkus.arc.processor.DotNames.STRING);
}

public boolean isContentTypeBuffer() {
Type type = getContentType();
if (type == null) {
return false;
}
return type.name().equals(DotName.createSimple(Buffer.class.getName()));
return type.name().equals(DotNames.BUFFER);
}

public boolean isContentTypeRxBuffer() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.vertx.web.deployment;

import java.util.Optional;
import java.util.function.Consumer;

import javax.enterprise.context.spi.Context;
Expand Down Expand Up @@ -29,6 +30,8 @@
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;

class Methods {
Expand All @@ -42,6 +45,21 @@ class Methods {

static final MethodDescriptor REQUEST = MethodDescriptor
.ofMethod(RoutingContext.class, "request", HttpServerRequest.class);
static final MethodDescriptor REQUEST_GET_PARAM = MethodDescriptor
.ofMethod(HttpServerRequest.class, "getParam", String.class, String.class);
static final MethodDescriptor REQUEST_GET_HEADER = MethodDescriptor
.ofMethod(HttpServerRequest.class, "getHeader", String.class, String.class);
static final MethodDescriptor GET_BODY = MethodDescriptor
.ofMethod(RoutingContext.class, "getBody", Buffer.class);
static final MethodDescriptor GET_BODY_AS_STRING = MethodDescriptor
.ofMethod(RoutingContext.class, "getBodyAsString", String.class);
static final MethodDescriptor GET_BODY_AS_JSON = MethodDescriptor
.ofMethod(RoutingContext.class, "getBodyAsJson", JsonObject.class);
static final MethodDescriptor GET_BODY_AS_JSON_ARRAY = MethodDescriptor
.ofMethod(RoutingContext.class, "getBodyAsJsonArray", JsonArray.class);
static final MethodDescriptor JSON_OBJECT_MAP_TO = MethodDescriptor
.ofMethod(JsonObject.class, "mapTo", Object.class, Class.class);

static final MethodDescriptor RESPONSE = MethodDescriptor
.ofMethod(RoutingContext.class, "response", HttpServerResponse.class);

Expand Down Expand Up @@ -140,6 +158,9 @@ class Methods {
static final MethodDescriptor ROUTE_HANDLERS_SET_CONTENT_TYPE = MethodDescriptor
.ofMethod(RouteHandlers.class, "setContentType", void.class, RoutingContext.class);

static final MethodDescriptor OPTIONAL_OF_NULLABLE = MethodDescriptor
.ofMethod(Optional.class, "ofNullable", Optional.class, Object.class);

private Methods() {
// Avoid direct instantiation
}
Expand Down
Loading

0 comments on commit 854b8a0

Please sign in to comment.