pekko/akka-docs/rst/java/http/routing-dsl/rejections.rst
Johan Andrén 29029be31d !htp #18919 #19519 New JavaDSL for Akka HTTP (#20518)
* !htt #18919 #19519 Align Java HTTP server DSL with Scala

This commits replaces the Java HTTP server DSL with a Java-8 centric one
which exposes all scala DSL concepts to be usable from Java, including
custom directives, (un)marshallers, rejections, headers, and type safety
for path and query parameters.

* Add RequestContext and RouteResult to Java DSL
fix websockets
WIP bring java docs up to date.
This applies some updates to the root-level documentation

* [htp] Fix java documentation to correctly mention timeouts

Timeouts are configured the same in Java and Scala. Hence, linking to the
scala docs for timeouts from Java.

* =htc fix optionalHeaderValueByType in Java

* =htt #20200 fix java testkit always using NoLogging instead logger

* +htt actually run new javadsl tests, allow overriding config

* =htt improve javadsl test infra with more details when fails

* =htt fix bug in wrong path matcher exposed

* +htp add missing remaining path matcher

* =htp Java DSL cookie tests fixed

* =htt Java DSL ParameterDirectivesTest fixed

Protect the tweets from scalariform

Incorrect response expectations in cache condition directives spec fixed

* =htt Path directives for Java DSL

* +!htt PathMatchers rewritten, made uniform and tests passing

* Bugfix in java reject and a little test-boyscouting

* Revert "Incorrect response expectations in cache condition directives spec fixed"

This reverts commit cd50e89d45db010309f8249b090ea654ebb11c7a.

* +htc HttpAPIsTest is compile time only, not for running
Also, moved from the client package since not strictly a client test.

SecurityDirectives passing

Two faulty tests and two actual bugs.

Fix for cache condition spec not working

* Not sending in Unit instad of the implicit magnet in the test
* HeaderMagnet now works as expected
* Java API added for - and + on DateTime

PetStore example and test fixed

* Annotations to make marshalling work without default constructor
* Made model class immutable

Incorrect tests fixed

Some scaladoc boyscouting as bonus

* =htt RequestValTest sprinkled out across multiple directive tests

Client ip extraction test with incorrect header name fixed.

* =htt Incorrect CodingDirectivesTest fixed.

* =htt Bugfix for Java Unmarshaller.firstOf and fixes to JavaRouteTest

* =htt MarshallerTest fixed

* Missing seal signature added to JavaDSL
* More consistent (with Scala) test kit setup for Java
* missing Javadocs added
* Thread.sleep in default exception handler removed

* =htt copy directive docs, prepare for finishing it up

* +htt SecurityDirectives.authorize variants and test coverage added

* +htt Custom headers in Java DSL

* =htt WIP on java docs

* +htp add missing parameterOrDefault directive
Fixed a lot of doc warnings

* =htc intense progress on javadsl docs

* =htc #20470 Link to issue about docs and fix compile error
compile, migration guide
don't mima check http-experimental

* =htt Java DSL doc warnings fixed.
Only `Could not lex literal_block` ones left now

* =htc fix mima settings

* =doc fix MethodDirectives doc test with custom method

* =htc fix coding directives spec after bad merge

* =htc fix concat being corresponding to route() in javadsl

* =htt Disable consistency check for route/concat as it fails only on ci server

* !htt Minor fixes to PathMatchers
2016-05-16 10:38:40 +02:00

137 lines
7.3 KiB
ReStructuredText

.. _rejections-java:
Rejections
==========
TODO update to Java APIs
In the chapter about constructing :ref:`Routes` the ``~`` operator was introduced, which connects two routes in a way
that allows a second route to get a go at a request if the first route "rejected" it. The concept of "rejections" is
used by Akka HTTP for maintaining a more functional overall architecture and in order to be able to properly
handle all kinds of error scenarios.
When a filtering directive, like the :ref:`-get-` directive, cannot let the request pass through to its inner route because
the filter condition is not satisfied (e.g. because the incoming request is not a GET request) the directive doesn't
immediately complete the request with an error response. Doing so would make it impossible for other routes chained in
after the failing filter to get a chance to handle the request.
Rather, failing filters "reject" the request in the same way as by explicitly calling ``requestContext.reject(...)``.
After having been rejected by a route the request will continue to flow through the routing structure and possibly find
another route that can complete it. If there are more rejections all of them will be picked up and collected.
If the request cannot be completed by (a branch of) the route structure an enclosing :ref:`-handleRejections-java-` directive
can be used to convert a set of rejections into an ``HttpResponse`` (which, in most cases, will be an error response).
``Route.seal`` internally wraps its argument route with the :ref:`-handleRejections-java-` directive in order to "catch"
and handle any rejection.
Predefined Rejections
---------------------
A rejection encapsulates a specific reason why a route was not able to handle a request. It is modeled as an object of
type ``Rejection``. Akka HTTP comes with a set of `predefined rejections`__, which are used by the many
:ref:`predefined directives <Predefined Directives-java>`.
Rejections are gathered up over the course of a Route evaluation and finally converted to ``HttpResponse`` replies by
the :ref:`-handleRejections-` directive if there was no way for the request to be completed.
__ @github@/akka-http/src/main/scala/akka/http/scaladsl/server/Rejection.scala
.. _The RejectionHandler-java:
The RejectionHandler
--------------------
The :ref:`-handleRejections-` directive delegates the actual job of converting a list of rejections to its argument, a
RejectionHandler__, which is defined like this::
trait RejectionHandler extends (immutable.Seq[Rejection] ⇒ Option[Route])
__ @github@/akka-http/src/main/scala/akka/http/scaladsl/server/RejectionHandler.scala
Since a ``RejectionHandler`` returns an ``Option[Route]`` it can choose whether it would like to handle the current set
of rejections or not. If it returns ``None`` the rejections will simply continue to flow through the route structure.
The default ``RejectionHandler`` applied by the top-level glue code that turns a ``Route`` into a
``Flow`` or async handler function for the :ref:`low-level API <http-low-level-server-side-api>` (via
``Route.handlerFlow`` or ``Route.asyncHandler``) will handle *all* rejections that reach it.
Rejection Cancellation
----------------------
As you can see from its definition above the ``RejectionHandler`` doesn't handle single rejections but a whole list of
them. This is because some route structure produce several "reasons" why a request could not be handled.
Take this route structure for example:
TODO missing sample
For uncompressed POST requests this route structure would initially yield two rejections:
- a ``MethodRejection`` produced by the :ref:`-get-` directive (which rejected because the request is not a GET request)
- an ``UnsupportedRequestEncodingRejection`` produced by the :ref:`-decodeRequestWith-` directive (which only accepts
gzip-compressed requests here)
In reality the route even generates one more rejection, a ``TransformationRejection`` produced by the :ref:`-post-`
directive. It "cancels" all other potentially existing *MethodRejections*, since they are invalid after the
:ref:`-post-` directive allowed the request to pass (after all, the route structure *can* deal with POST requests).
These types of rejection cancellations are resolved *before* a ``RejectionHandler`` sees the rejection list.
So, for the example above the ``RejectionHandler`` will be presented with only a single-element rejection list,
containing nothing but the ``UnsupportedRequestEncodingRejection``.
.. _Empty Rejections-java:
Empty Rejections
----------------
Since rejections are passed around in a list (or rather immutable ``Seq``) you might ask yourself what the semantics of
an empty rejection list are. In fact, empty rejection lists have well defined semantics. They signal that a request was
not handled because the respective resource could not be found. Akka HTTP reserves the special status of "empty
rejection" to this most common failure a service is likely to produce.
So, for example, if the :ref:`-path-` directive rejects a request it does so with an empty rejection list. The
:ref:`-host-` directive behaves in the same way.
Customizing Rejection Handling
------------------------------
If you'd like to customize the way certain rejections are handled you'll have to write a custom
:ref:`RejectionHandler <The RejectionHandler>`. Here is an example:
TODO missing sample
The easiest way to construct a ``RejectionHandler`` is via the ``RejectionHandler.Builder`` that Akka HTTP provides.
After having created a new ``Builder`` instance with ``RejectionHandler.newBuilder()``
you can attach handling logic for certain types of rejections through three helper methods:
handle
Handles certain rejections with the given partial function. The partial function simply produces a ``Route`` which is
run when the rejection is "caught". This makes the full power of the Routing DSL available for defining rejection
handlers and even allows for recursing back into the main route structure if required.
handleAll[T <: Rejection]
Handles all rejections of a certain type at the same time. This is useful for cases where your need access to more
than the first rejection of a certain type, e.g. for producing the error message to an unsupported request method.
handleNotFound
As described :ref:`above <Empty Rejections>` "Resource Not Found" is special as it is represented with an empty
rejection set. The ``handleNotFound`` helper let's you specify the "recovery route" for this case.
Even though you could handle several different rejection types in a single partial function supplied to ``handle``
it is recommended to split these up into distinct ``handle`` attachments instead.
This way the priority between rejections is properly defined via the order of your ``handle`` clauses rather than the
(sometimes hard to predict or control) order of rejections in the rejection set.
Once you have defined your custom ``RejectionHandler`` you have two options for "activating" it:
1. Bring it into implicit scope at the top-level.
2. Supply it as argument to the :ref:`-handleRejections-` directive.
In the first case your handler will be "sealed" (which means that it will receive the default handler as a fallback for
all cases your handler doesn't handle itself) and used for all rejections that are not handled within the route structure
itself.
The second case allows you to restrict the applicability of your handler to certain branches of your route structure.