This entails:
* adding akka.pattern.PatternCS.* to enable ask etc. with
CompletionStage
* changing RequestContext to offer an ExecutionContextExecutor for the
CompletionStage.*Async combinators
* splitting up akka.stream.Queue for JavaDSL consistency
90 lines
3.8 KiB
ReStructuredText
90 lines
3.8 KiB
ReStructuredText
.. _routes-java:
|
|
|
|
Routes
|
|
======
|
|
|
|
A ``Route`` itself is a function that operates on a ``RequestContext`` and returns a ``RouteResult``. The
|
|
``RequestContext`` is a data structure that contains the current request and auxiliary data like the so far unmatched
|
|
path of the request URI that gets passed through the route structure. It also contains the current ``ExecutionContext``
|
|
and ``akka.stream.Materializer``, so that these don't have to be passed around manually.
|
|
|
|
|
|
.. _request-context-java:
|
|
|
|
RequestContext
|
|
--------------
|
|
|
|
The ``RequestContext`` achieves two goals: it allows access to request data and it is a factory for creating a
|
|
``RouteResult``. A user-defined handler (see :ref:`handlers-java`) that is usually used at the leaf position of
|
|
the route tree receives a ``RequestContext``, evaluates its content and then returns a result generated by one of
|
|
the methods of the context.
|
|
|
|
.. _route-result-java:
|
|
|
|
RouteResult
|
|
-----------
|
|
|
|
The ``RouteResult`` is an opaque structure that represents possible results of evaluating a route. A ``RouteResult``
|
|
can only be created by using one of the methods of the ``RequestContext``. A result can either be a response, if
|
|
it was generated by one of the ``completeX`` methods, it can be an eventual result, i.e. a ``CompletionStage<RouteResult`` if
|
|
``completeWith`` was used or a rejection that contains information about why the route could not handle the request.
|
|
|
|
|
|
Composing Routes
|
|
----------------
|
|
|
|
Routes are composed to form the route tree in two principle ways.
|
|
|
|
A route can be wrapped by a "Directive" which adds some behavioral aspect to its wrapped "inner route". Such an aspect can
|
|
be
|
|
|
|
* filtering requests to decide which requests will get to the inner route
|
|
* transforming the request before passing it to the inner route
|
|
* transforming the response (or more generally the route result) received from the inner route
|
|
* applying side-effects around inner route processing, such as measuring the time taken to run the inner route
|
|
|
|
akka-http defines a library of predefined :ref:`directives-java` and routes for all the various aspects of dealing with
|
|
HTTP requests and responses.
|
|
|
|
The other way of composition is defining a list of ``Route`` alternatives. Alternative routes are tried one after
|
|
the other until one route "accepts" the request and provides a response. Otherwise, a route can also "reject" a request,
|
|
in which case further alternatives are explored. Alternatives are specified by passing a list of routes either
|
|
to ``Directive.route()`` as in ``path("xyz").route()`` or to directives that directly take a variable number
|
|
of inner routes as argument like ``get()``.
|
|
|
|
.. _The Routing Tree-java:
|
|
|
|
The Routing Tree
|
|
----------------
|
|
|
|
Essentially, when you combine routes via nesting and alternative, you build a routing
|
|
structure that forms a tree. When a request comes in it is injected into this tree at the root and flows down through
|
|
all the branches in a depth-first manner until either some node completes it or it is fully rejected.
|
|
|
|
Consider this schematic example::
|
|
|
|
val route =
|
|
a.route(
|
|
b.route(
|
|
c.route(
|
|
... // route 1
|
|
),
|
|
d.route(
|
|
... // route 2
|
|
),
|
|
... // route 3
|
|
),
|
|
e.route(
|
|
... // route 4
|
|
)
|
|
)
|
|
|
|
Here five directives form a routing tree.
|
|
|
|
- Route 1 will only be reached if directives ``a``, ``b`` and ``c`` all let the request pass through.
|
|
- Route 2 will run if ``a`` and ``b`` pass, ``c`` rejects and ``d`` passes.
|
|
- Route 3 will run if ``a`` and ``b`` pass, but ``c`` and ``d`` reject.
|
|
|
|
Route 3 can therefore be seen as a "catch-all" route that only kicks in, if routes chained into preceding positions
|
|
reject. This mechanism can make complex filtering logic quite easy to implement: simply put the most
|
|
specific cases up front and the most general cases in the back.
|