diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java index 5a674e387624c..e957143cd6ee8 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java @@ -232,14 +232,18 @@ public class HttpConfiguration { public boolean enableDecompression; /** - * Provides a hint (optional) for the contents type of responses generated for + * Provides a hint (optional) for the default content type of responses generated for * the errors not handled by the application. *

- * When unset, Quarkus will decide which contents type to use based on request headers. + * If the client requested a supported content-type in request headers + * (e.g. "Accept: application/json", "Accept: text/html"), + * Quarkus will use that content type. + *

+ * Otherwise, it will default to the content type configured here. *

*/ @ConfigItem - public Optional unhandledErrorContentsType; + public Optional unhandledErrorContentTypeDefault; public ProxyConfig proxy; diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java index de7b78350be60..bb7b76d586dc2 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java @@ -40,11 +40,11 @@ public class QuarkusErrorHandler implements Handler { private static final AtomicLong ERROR_COUNT = new AtomicLong(); private final boolean showStack; - private final Optional contentTypeHint; + private final Optional contentTypeDefault; - public QuarkusErrorHandler(boolean showStack, Optional contentTypeHint) { + public QuarkusErrorHandler(boolean showStack, Optional contentTypeDefault) { this.showStack = showStack; - this.contentTypeHint = contentTypeHint; + this.contentTypeDefault = contentTypeDefault; } @Override @@ -139,17 +139,6 @@ public void accept(Throwable throwable) { if (responseContentType == null) { responseContentType = ""; } - if (contentTypeHint.isPresent()) { - switch (contentTypeHint.get()) { - case JSON: - jsonResponse(event, ContentTypes.APPLICATION_JSON, details, stack); - break; - case HTML: - htmlResponse(event, details, exception); - break; - } - return; - } switch (responseContentType) { case ContentTypes.TEXT_HTML: case ContentTypes.APPLICATION_XHTML: @@ -161,9 +150,16 @@ public void accept(Throwable throwable) { case ContentTypes.TEXT_JSON: jsonResponse(event, responseContentType, details, stack); break; - // We default to HTML representation default: - htmlResponse(event, details, exception); + // We default to HTML representation + switch (contentTypeDefault.orElse(HttpConfiguration.PayloadHint.HTML)) { + case JSON: + jsonResponse(event, ContentTypes.APPLICATION_JSON, details, stack); + break; + case HTML: + htmlResponse(event, details, exception); + break; + } break; } } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java index 5376e870eb2b1..4f351e10fe299 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java @@ -309,7 +309,7 @@ public void finalizeRouter(BeanContainer container, Consumer defaultRoute } httpRouteRouter.route().last().failureHandler( - new QuarkusErrorHandler(launchMode.isDevOrTest(), httpConfiguration.unhandledErrorContentsType)); + new QuarkusErrorHandler(launchMode.isDevOrTest(), httpConfiguration.unhandledErrorContentTypeDefault)); if (requireBodyHandler) { //if this is set then everything needs the body handler installed diff --git a/extensions/vertx-http/runtime/src/test/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandlerTest.java b/extensions/vertx-http/runtime/src/test/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandlerTest.java index 1a37d1539c8a0..f1634fee29f5f 100644 --- a/extensions/vertx-http/runtime/src/test/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandlerTest.java +++ b/extensions/vertx-http/runtime/src/test/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandlerTest.java @@ -50,16 +50,27 @@ public void string_with_quotes_should_be_correctly_escaped() { } @Test - public void json_content_type_hint_should_be_respected() { + public void json_content_type_hint_should_be_respected_if_not_accepted() { QuarkusErrorHandler errorHandler = new QuarkusErrorHandler(false, Optional.of(HttpConfiguration.PayloadHint.JSON)); Mockito.when(routingContext.failure()).thenReturn(testError); Mockito.when(routingContext.parsedHeaders().findBestUserAcceptedIn(any(), any())) - .thenReturn(new ParsableMIMEValue("text/html").forceParse()); + .thenReturn(new ParsableMIMEValue("application/foo+json").forceParse()); errorHandler.handle(routingContext); Mockito.verify(routingContext.response().headers()).set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=utf-8"); } + @Test + public void json_content_type_hint_should_be_ignored_if_accepted() { + QuarkusErrorHandler errorHandler = new QuarkusErrorHandler(false, Optional.of(HttpConfiguration.PayloadHint.JSON)); + Mockito.when(routingContext.failure()).thenReturn(testError); + Mockito.when(routingContext.parsedHeaders().findBestUserAcceptedIn(any(), any())) + .thenReturn(new ParsableMIMEValue("text/html").forceParse()); + errorHandler.handle(routingContext); + Mockito.verify(routingContext.response().headers()).set(HttpHeaderNames.CONTENT_TYPE, + "text/html; charset=utf-8"); + } + @Test public void content_type_should_default_to_json_if_accepted() { QuarkusErrorHandler errorHandler = new QuarkusErrorHandler(false, Optional.empty()); @@ -72,15 +83,26 @@ public void content_type_should_default_to_json_if_accepted() { } @Test - public void html_content_type_hint_should_be_respected() { + public void html_content_type_hint_should_be_respected_if_not_accepted() { QuarkusErrorHandler errorHandler = new QuarkusErrorHandler(false, Optional.of(HttpConfiguration.PayloadHint.HTML)); Mockito.when(routingContext.failure()).thenReturn(testError); Mockito.when(routingContext.parsedHeaders().findBestUserAcceptedIn(any(), any())) - .thenReturn(new ParsableMIMEValue("application/json").forceParse()); + .thenReturn(new ParsableMIMEValue("application/foo+json").forceParse()); errorHandler.handle(routingContext); Mockito.verify(routingContext.response().headers()).set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=utf-8"); } + @Test + public void html_content_type_hint_should_be_ignored_if_accepted() { + QuarkusErrorHandler errorHandler = new QuarkusErrorHandler(false, Optional.of(HttpConfiguration.PayloadHint.HTML)); + Mockito.when(routingContext.failure()).thenReturn(testError); + Mockito.when(routingContext.parsedHeaders().findBestUserAcceptedIn(any(), any())) + .thenReturn(new ParsableMIMEValue("application/json").forceParse()); + errorHandler.handle(routingContext); + Mockito.verify(routingContext.response().headers()).set(HttpHeaderNames.CONTENT_TYPE, + "application/json; charset=utf-8"); + } + @Test public void content_type_should_default_to_html_if_accepted() { QuarkusErrorHandler errorHandler = new QuarkusErrorHandler(false, Optional.empty());