Skip to content

Commit

Permalink
Reactive routes - inject throwable for a failure handler
Browse files Browse the repository at this point in the history
- resolves #12249
  • Loading branch information
mkouba committed Sep 23, 2020
1 parent 77b772c commit e54efbe
Show file tree
Hide file tree
Showing 9 changed files with 384 additions and 97 deletions.
12 changes: 12 additions & 0 deletions docs/src/main/asciidoc/reactive-routes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,18 @@ Person createPerson(@Body Person person, @Param("id") Optional<String> primaryKe
}
----
A failure handler can declare a single method parameter whose type extends `Throwable`.
The type of the parameter is used to match the result of `RoutingContext#failure()`.
.Failure Handler Example
[source,java]
----
@Route(type = HandlerType.FAILURE)
void unsupported(UnsupportedOperationException e, HttpServerResponse response) {
response.setStatusCode(501).end(e.getMessage());
}
----
=== Returning Unis
In a reactive route, you can return a `Uni` directly:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,7 @@ final class DotNames {
static final DotName JSON_OBJECT = DotName.createSimple(JsonObject.class.getName());
static final DotName JSON_ARRAY = DotName.createSimple(JsonArray.class.getName());
static final DotName LIST = DotName.createSimple(List.class.getName());
static final DotName EXCEPTION = DotName.createSimple(Exception.class.getName());
static final DotName THROWABLE = DotName.createSimple(Throwable.class.getName());

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.jboss.jandex.Type;

import io.quarkus.hibernate.validator.spi.BeanValidationAnnotationsBuildItem;
import io.quarkus.vertx.http.runtime.HandlerType;

/**
* Describe a request handler.
Expand All @@ -13,10 +14,12 @@ class HandlerDescriptor {

private final MethodInfo method;
private final BeanValidationAnnotationsBuildItem validationAnnotations;
private final HandlerType handlerType;

HandlerDescriptor(MethodInfo method, BeanValidationAnnotationsBuildItem bvAnnotations) {
HandlerDescriptor(MethodInfo method, BeanValidationAnnotationsBuildItem bvAnnotations, HandlerType handlerType) {
this.method = method;
this.validationAnnotations = bvAnnotations;
this.handlerType = handlerType;
}

Type getReturnType() {
Expand Down Expand Up @@ -111,4 +114,8 @@ boolean isContentTypeMutinyBuffer() {
return type.name().equals(DotNames.MUTINY_BUFFER);
}

HandlerType getHandlerType() {
return handlerType;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ class Methods {
.ofMethod(HttpServerRequest.class, "params", MultiMap.class);
static final MethodDescriptor REQUEST_HEADERS = MethodDescriptor
.ofMethod(HttpServerRequest.class, "headers", MultiMap.class);

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

static final MethodDescriptor FAIL = MethodDescriptor
.ofMethod(RoutingContext.class, "fail", Void.TYPE, Throwable.class);
static final MethodDescriptor FAILURE = MethodDescriptor
.ofMethod(RoutingContext.class, "failure", Throwable.class);
static final MethodDescriptor NEXT = MethodDescriptor
.ofMethod(RoutingContext.class, "next", void.class);

static final MethodDescriptor UNI_SUBSCRIBE = MethodDescriptor.ofMethod(Uni.class, "subscribe", UniSubscribe.class);
static final MethodDescriptor UNI_SUBSCRIBE_WITH = MethodDescriptor
Expand Down Expand Up @@ -191,6 +193,10 @@ class Methods {
Object.class, Class[].class);
static final MethodDescriptor SET_IS_EMPTY = MethodDescriptor.ofMethod(Set.class, "isEmpty", Boolean.TYPE);

static final MethodDescriptor IS_ASSIGNABLE_FROM = MethodDescriptor.ofMethod(Class.class, "isAssignableFrom",
boolean.class, Class.class);
static final MethodDescriptor GET_CLASS = MethodDescriptor.ofMethod(Object.class, "getClass", Class.class);

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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.quarkus.vertx.web.failure;

import static io.quarkus.vertx.web.Route.HandlerType.FAILURE;
import static io.restassured.RestAssured.get;
import static org.hamcrest.Matchers.is;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.vertx.web.Param;
import io.quarkus.vertx.web.Route;
import io.vertx.core.http.HttpServerResponse;

public class FailureHandlerTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(Routes.class));

@Test
public void test() {
get("/fail?type=unsupported").then().statusCode(501).body(is("Unsupported!"));
get("/fail").then().statusCode(500).body(is("Unknown!"));
}

public static class Routes {

@Route
void fail(@Param String type) {
if ("unsupported".equals(type)) {
throw new UnsupportedOperationException("Unsupported!");
} else {
throw new RuntimeException("Unknown!");
}
}

@Route(path = "/fail", type = FAILURE, order = 1)
void uoe(UnsupportedOperationException e, HttpServerResponse response) {
response.setStatusCode(501).end(e.getMessage());
}

@Route(path = "/*", type = FAILURE, order = 2)
void re(RuntimeException e, HttpServerResponse response) {
response.setStatusCode(500).end(e.getMessage());
}

}

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

import static org.junit.jupiter.api.Assertions.fail;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.vertx.web.Param;
import io.quarkus.vertx.web.Route;
import io.quarkus.vertx.web.Route.HandlerType;

public class MultipleThrowableParamsFailureHandlerTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(Routes.class))
.setExpectedException(IllegalStateException.class);

@Test
public void testValidationFailed() {
fail();
}

public static class Routes {

@Route(type = HandlerType.FAILURE)
void fail(UnsupportedOperationException e1, @Param String type, RuntimeException e2) {
throw new RuntimeException("Unknown!");
}

}

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

import static org.junit.jupiter.api.Assertions.fail;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.vertx.web.Param;
import io.quarkus.vertx.web.Route;

public class ThrowableParamNormalHandlerTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(Routes.class))
.setExpectedException(IllegalStateException.class);

@Test
public void testValidationFailed() {
fail();
}

public static class Routes {

@Route
void fail(UnsupportedOperationException e, @Param String type) {
throw new RuntimeException("Unknown!");
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,32 @@ enum HandlerType {
*/
BLOCKING,
/**
* A failure handler.
*
* A failure handler can declare a single method parameter whose type extends {@link Throwable}. The type of the
* parameter is used to match the result of {@link RoutingContext#failure()}.
*
* <pre>
* <code>
* class Routes {
* {@literal @Route(type = HandlerType.FAILURE)}
* void unsupported(UnsupportedOperationException e, HttpServerResponse response) {
* response.setStatusCode(501).end(e.getMessage());
* }
* }
* </code>
* </pre>
*
* @see io.vertx.ext.web.Route#failureHandler(Handler)
*/
FAILURE
FAILURE;

public static HandlerType from(String value) {
for (HandlerType handlerType : values()) {
if (handlerType.toString().equals(value)) {
return handlerType;
}
}
return null;
}

}

Expand Down

0 comments on commit e54efbe

Please sign in to comment.