diff --git a/vertx-web/src/main/java/io/vertx/ext/web/Router.java b/vertx-web/src/main/java/io/vertx/ext/web/Router.java index dc306a7106..7a9d73b89f 100644 --- a/vertx-web/src/main/java/io/vertx/ext/web/Router.java +++ b/vertx-web/src/main/java/io/vertx/ext/web/Router.java @@ -372,6 +372,18 @@ static Router router(Vertx vertx) { @Fluent Router errorHandler(int statusCode, Handler errorHandler); + /** + * Specify an handler to handle an error for any status code that doesn't have a specific handler assigned. + * The handler will be called when the context fails and other failure handlers didn't write the reply or when an exception is thrown inside an handler. + * You must not use {@link RoutingContext#next()} inside the error handler + * This does not affect the normal failure routing logic. + * + * @param errorHandler error handler. Note: You must not use {@link RoutingContext#next()} inside the provided handler + * @return a reference to this, so the API can be used fluently + */ + @Fluent + Router uncaughtErrorHandler(Handler errorHandler); + /** * Used to route a context to the router. Used for sub-routers. You wouldn't normally call this method directly. * diff --git a/vertx-web/src/main/java/io/vertx/ext/web/impl/RouterImpl.java b/vertx-web/src/main/java/io/vertx/ext/web/impl/RouterImpl.java index 416584a581..2fc1ccdd8f 100644 --- a/vertx-web/src/main/java/io/vertx/ext/web/impl/RouterImpl.java +++ b/vertx-web/src/main/java/io/vertx/ext/web/impl/RouterImpl.java @@ -297,6 +297,12 @@ public synchronized Router errorHandler(int statusCode, Handler return this; } + @Override + public synchronized Router uncaughtErrorHandler(Handler errorHandler) { + state = state.setUncaughtErrorHandler(errorHandler); + return this; + } + synchronized void add(RouteImpl route) { state = state.addRoute(route); // notify the listeners as the routes are changed diff --git a/vertx-web/src/main/java/io/vertx/ext/web/impl/RouterState.java b/vertx-web/src/main/java/io/vertx/ext/web/impl/RouterState.java index 0d08a7a172..e7e4f625b5 100644 --- a/vertx-web/src/main/java/io/vertx/ext/web/impl/RouterState.java +++ b/vertx-web/src/main/java/io/vertx/ext/web/impl/RouterState.java @@ -57,15 +57,17 @@ final class RouterState { private final TreeSet routes; private final int orderSequence; private final Map> errorHandlers; + private final Handler uncaughtErrorHandler; private final Handler modifiedHandler; private final AllowForwardHeaders allowForward; private final Map metadata; - public RouterState(RouterImpl router, TreeSet routes, int orderSequence, Map> errorHandlers, Handler modifiedHandler, AllowForwardHeaders allowForward, Map metadata) { + public RouterState(RouterImpl router, TreeSet routes, int orderSequence, Map> errorHandlers, final Handler uncaughtErrorHandler, Handler modifiedHandler, AllowForwardHeaders allowForward, Map metadata) { this.router = router; this.routes = routes; this.orderSequence = orderSequence; this.errorHandlers = errorHandlers; + this.uncaughtErrorHandler = uncaughtErrorHandler; this.modifiedHandler = modifiedHandler; this.allowForward = allowForward; this.metadata = metadata; @@ -78,6 +80,7 @@ public RouterState(RouterImpl router) { 0, null, null, + null, AllowForwardHeaders.NONE, null); } @@ -99,6 +102,7 @@ RouterState setRoutes(Set routes) { new TreeSet<>(routeComparator), this.orderSequence, this.errorHandlers, + this.uncaughtErrorHandler, this.modifiedHandler, this.allowForward, this.metadata); @@ -119,6 +123,7 @@ RouterState addRoute(RouteImpl route) { routes, this.orderSequence, this.errorHandlers, + this.uncaughtErrorHandler, this.modifiedHandler, this.allowForward, this.metadata); @@ -130,6 +135,7 @@ RouterState clearRoutes() { new TreeSet<>(routeComparator), this.orderSequence, this.errorHandlers, + null, this.modifiedHandler, this.allowForward, this.metadata); @@ -147,6 +153,7 @@ RouterState removeRoute(RouteImpl route) { routes, this.orderSequence, this.errorHandlers, + this.uncaughtErrorHandler, this.modifiedHandler, this.allowForward, this.metadata); @@ -162,6 +169,7 @@ RouterState incrementOrderSequence() { this.routes, this.orderSequence + 1, this.errorHandlers, + this.uncaughtErrorHandler, this.modifiedHandler, this.allowForward, this.metadata); @@ -173,6 +181,7 @@ RouterState setOrderSequence(int orderSequence) { this.routes, orderSequence, this.errorHandlers, + this.uncaughtErrorHandler, this.modifiedHandler, this.allowForward, this.metadata); @@ -188,6 +197,7 @@ RouterState setErrorHandlers(Map> errorHandlers this.routes, this.orderSequence, errorHandlers, + this.uncaughtErrorHandler, this.modifiedHandler, this.allowForward, this.metadata); @@ -195,9 +205,12 @@ RouterState setErrorHandlers(Map> errorHandlers Handler getErrorHandler(int errorCode) { if (errorHandlers != null) { - return errorHandlers.get(errorCode); + final Handler errorHandler = errorHandlers.get(errorCode); + if (errorHandler != null) { + return errorHandler; + } } - return null; + return uncaughtErrorHandler; } RouterState putErrorHandler(int errorCode, Handler errorHandler) { @@ -206,6 +219,7 @@ RouterState putErrorHandler(int errorCode, Handler errorHandler) this.routes, this.orderSequence, this.errorHandlers == null ? new HashMap<>() : new HashMap<>(errorHandlers), + this.uncaughtErrorHandler, this.modifiedHandler, this.allowForward, this.metadata); @@ -214,6 +228,18 @@ RouterState putErrorHandler(int errorCode, Handler errorHandler) return newState; } + RouterState setUncaughtErrorHandler(Handler errorHandler) { + return new RouterState( + this.router, + this.routes, + this.orderSequence, + this.errorHandlers, + errorHandler, + this.modifiedHandler, + this.allowForward, + this.metadata); + } + public Handler getModifiedHandler() { return modifiedHandler; } @@ -224,6 +250,7 @@ public RouterState setModifiedHandler(Handler modifiedHandler) { this.routes, this.orderSequence, this.errorHandlers, + this.uncaughtErrorHandler, modifiedHandler, this.allowForward, this.metadata); @@ -235,6 +262,7 @@ public RouterState setAllowForward(AllowForwardHeaders allow) { this.routes, this.orderSequence, this.errorHandlers, + this.uncaughtErrorHandler, this.modifiedHandler, allow, this.metadata); @@ -256,6 +284,7 @@ public RouterState putMetadata(String key, Object value) { this.routes, this.orderSequence, this.errorHandlers, + this.uncaughtErrorHandler, this.modifiedHandler, this.allowForward, Collections.unmodifiableMap(metadata)); diff --git a/vertx-web/src/test/java/io/vertx/ext/web/tests/RouterTest.java b/vertx-web/src/test/java/io/vertx/ext/web/tests/RouterTest.java index 3ba4ebd556..0ae9c8d358 100644 --- a/vertx-web/src/test/java/io/vertx/ext/web/tests/RouterTest.java +++ b/vertx-web/src/test/java/io/vertx/ext/web/tests/RouterTest.java @@ -3693,6 +3693,14 @@ public void testExtractCallee() throws Exception { 200, "OK"); } + @Test + public void testUncaughtErrorHandler() throws Exception { + router.route().consumes("text/html").handler(rc -> rc.response().end()); + router.uncaughtErrorHandler(context -> context.response().setStatusCode(context.statusCode()).setStatusMessage("Dumb").end()); + + testRequestWithContentType(HttpMethod.GET, "/foo", "something/html", 415, "Dumb"); + } + @Test public void testErrorHandlerInvokedOnce() throws Exception { AtomicInteger errorHandlerInvocations = new AtomicInteger();