@@ -505,23 +505,25 @@ interface Before {
505505
506506The javadoc:Route.After[text=after] filter runs after a `handler`.
507507
508- An `after` filter takes two arguments. The first argument is the `HTTP context`, while the second
509- argument is the result/response from a **functional handler** or `null` for **side-effects** handler.
508+ An `after` filter takes three arguments. The first argument is the `HTTP context`, the second
509+ argument is the result/response from a **functional handler** or `null` for **side-effects** handler,
510+ the third and last argument is an exception generates from handler.
511+
510512It expected to operates via side effects, usually modifying the HTTP response (if possible) or
511513for cleaning/trace execution.
512514
513515[source,java]
514516----
515517interface After {
516- void apply(Context ctx, Object result);
518+ void apply(Context ctx, Object result, Throwable failure );
517519}
518520----
519521
520522.Functional Handler:
521523[source,java,role="primary"]
522524----
523525{
524- after((ctx, result) -> {
526+ after((ctx, result, failure ) -> {
525527 System.out.println(result); <1>
526528 ctx.setResponseHeader("foo", "bar"); <2>
527529 });
@@ -558,7 +560,7 @@ For **side effects** handler the after filter is invoked with a `null` value and
558560[source,java,role="primary"]
559561----
560562{
561- after((ctx, result) -> {
563+ after((ctx, result, failure ) -> {
562564 System.out.println(result); <1>
563565 ctx.setResponseHeader("foo", "bar"); <2>
564566 });
@@ -597,7 +599,7 @@ You can check whenever you can modify the response by checking the state of java
597599[source,java,role="primary"]
598600----
599601{
600- after((ctx, result) -> {
602+ after((ctx, result, failure ) -> {
601603 if (ctx.isResponseStarted()) {
602604 // Don't modify response
603605 } else {
@@ -621,6 +623,119 @@ You can check whenever you can modify the response by checking the state of java
621623}
622624----
623625
626+ [NOTE]
627+ ====
628+ An after handler is always invoked.
629+ ====
630+
631+ The next examples demonstrate some use cases for dealing with errored responses, but keep in mind
632+ that an after handler is not a mechanism for handling and reporting exceptions that's is a task for an <<error-handler, Error Handler>>.
633+
634+ .Run code depending of success or failure responses:
635+ [source,java,role="primary"]
636+ ----
637+ {
638+ after((ctx, result, failure) -> {
639+ if (failure == null) {
640+ db.commit(); <1>
641+ } else {
642+ db.rollback(); <2>
643+ }
644+ });
645+ }
646+ ----
647+
648+ .Kotlin
649+ [source,kotlin,role="secondary"]
650+ ----
651+ {
652+ after {
653+ if (failure == null) {
654+ db.commit() <1>
655+ } else {
656+ db.rollback() <2>
657+ }
658+ }
659+ }
660+ ----
661+
662+ Here the exception is still propagated given the chance to the <<error-handler, Error Handler>> to jump in.
663+
664+ .Recover fom exception and produces an alternative output:
665+ [source,java,role="primary"]
666+ ----
667+ {
668+ after((ctx, result, failure) -> {
669+ if (failure instanceOf MyBusinessException) {
670+ ctx.send("Recovering from something"); <1>
671+ }
672+ });
673+ }
674+ ----
675+
676+ .Kotlin
677+ [source,kotlin,role="secondary"]
678+ ----
679+ {
680+ after {
681+ if (failure is MyBusinessException) {
682+ ctx.send("Recovering from something") <1>
683+ }
684+ }
685+ }
686+ ----
687+
688+ <1> Recover and produces an alternative output
689+
690+ Here the exception wont be propagated due we produces a response, so error handler won't be execute it.
691+
692+ In case where the after handler produces a new exception, that exception will be add to the original exception as suppressed exception.
693+
694+ .Suppressed exceptions:
695+ [source,java,role="primary"]
696+ ----
697+ {
698+ after((ctx, result, failure) -> {
699+ ...
700+ throw new AnotherException();
701+ });
702+
703+ get("/", ctx -> {
704+ ...
705+ throw new OriginalException();
706+ });
707+
708+ error((ctx, failure, code) -> {
709+ Throwable originalException = failure; <1>
710+ Throwable anotherException = failure.getSuppressed()[0]; <2>
711+ });
712+ }
713+ ----
714+
715+ .Kotlin
716+ [source,kotlin,role="secondary"]
717+ ----
718+ {
719+ after {
720+ ...
721+ throw AnotherException();
722+ }
723+
724+ get("/") { ctx ->
725+ ...
726+ throw OriginalException()
727+ }
728+
729+ error { ctx, failure, code) ->
730+ val originalException = failure <1>
731+ val anotherException = failure.getSuppressed()[0] <2>
732+ }
733+ }
734+ ----
735+
736+ <1> Will be `OriginalException`
737+ <2> Will be `AnotherException`
738+
624739=== Pipeline
625740
626741Route pipeline (a.k.a route stack) is a composition of one or more decorator(s) tied to a single `handler`:
0 commit comments