=doc add lots of documentation about java-side routing API, some detail and further examples still missing

This commit is contained in:
Johannes Rudolph 2015-07-10 19:10:29 +02:00
parent 11d98a6593
commit ab38c36cba
20 changed files with 384 additions and 50 deletions

View file

@ -0,0 +1,68 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.javadsl;
//#high-level-server-example
import akka.actor.ActorSystem;
import akka.http.javadsl.model.MediaTypes;
import akka.http.javadsl.server.*;
import akka.http.javadsl.server.values.Parameters;
import java.io.IOException;
public class HighLevelServerExample extends HttpApp {
public static void main(String[] args) throws IOException {
// boot up server using the route as defined below
ActorSystem system = ActorSystem.create();
// HttpApp.bindRoute expects a route being provided by HttpApp.createRoute
new HighLevelServerExample().bindRoute("localhost", 8080, system);
System.out.println("Type RETURN to exit");
System.in.read();
system.shutdown();
}
// A RequestVal is a type-safe representation of some aspect of the request.
// In this case it represents the `name` URI parameter of type String.
private RequestVal<String> name = Parameters.stringValue("name").withDefault("Mister X");
@Override
public Route createRoute() {
// This handler generates responses to `/hello?name=XXX` requests
Route helloRoute =
handleWith(name,
// in Java 8 the following becomes simply
// (ctx, name) -> ctx.complete("Hello " + name + "!")
new Handler1<String>() {
@Override
public RouteResult handle(RequestContext ctx, String name) {
return ctx.complete("Hello " + name + "!");
}
});
return
// here the complete behavior for this server is defined
route(
// only handle GET requests
get(
// matches the empty path
pathSingleSlash().route(
// return a constant string with a certain content type
complete(MediaTypes.TEXT_HTML.toContentType(),
"<html><body>Hello world!</body></html>")
),
path("ping").route(
// return a simple `text/plain` response
complete("PONG!")
),
path("hello").route(
// uses the route defined above
helloRoute
)
)
);
}
}
//#high-level-server-example

View file

@ -5,6 +5,7 @@
package docs.http.javadsl;
import akka.actor.ActorSystem;
import akka.http.impl.util.Util;
import akka.http.javadsl.Http;
import akka.http.javadsl.IncomingConnection;
import akka.http.javadsl.ServerBinding;
@ -28,7 +29,7 @@ import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;
public class HttpServerExampleSpec {
public void bindingExample() {
public static void bindingExample() {
//#binding-example
ActorSystem system = ActorSystem.create();
Materializer materializer = ActorMaterializer.create(system);
@ -47,50 +48,74 @@ public class HttpServerExampleSpec {
})).run(materializer);
//#binding-example
}
public void fullServerExample() {
public static void fullServerExample() throws Exception {
//#full-server-example
ActorSystem system = ActorSystem.create();
final Materializer materializer = ActorMaterializer.create(system);
ActorSystem system = ActorSystem.create();
//#full-server-example
try {
//#full-server-example
final Materializer materializer = ActorMaterializer.create(system);
Source<IncomingConnection, Future<ServerBinding>> serverSource =
Http.get(system).bind("localhost", 8080, materializer);
Source<IncomingConnection, Future<ServerBinding>> serverSource =
Http.get(system).bind("localhost", 8080, materializer);
final Function<HttpRequest, HttpResponse> requestHandler =
new Function<HttpRequest, HttpResponse>() {
private final HttpResponse NOT_FOUND =
HttpResponse.create()
.withStatus(404)
.withEntity("Unknown resource!");
//#request-handler
final Function<HttpRequest, HttpResponse> requestHandler =
new Function<HttpRequest, HttpResponse>() {
private final HttpResponse NOT_FOUND =
HttpResponse.create()
.withStatus(404)
.withEntity("Unknown resource!");
@Override
public HttpResponse apply(HttpRequest request) throws Exception {
if (request.method() == HttpMethods.GET) {
if (request.getUri().path().equals("/"))
return
HttpResponse.create()
.withEntity(MediaTypes.TEXT_HTML.toContentType(),
"<html><body>Hello world!</body></html>");
else if (request.getUri().path().equals("/ping"))
return HttpResponse.create().withEntity("PONG!");
else
return NOT_FOUND;
}
else return NOT_FOUND;
}
};
Future<ServerBinding> serverBindingFuture =
serverSource.to(Sink.foreach(
new Procedure<IncomingConnection>() {
@Override
public void apply(IncomingConnection connection) throws Exception {
System.out.println("Accepted new connection from " + connection.remoteAddress());
@Override
public HttpResponse apply(HttpRequest request) throws Exception {
Uri uri = request.getUri();
if (request.method() == HttpMethods.GET) {
if (uri.path().equals("/"))
return
HttpResponse.create()
.withEntity(MediaTypes.TEXT_HTML.toContentType(),
"<html><body>Hello world!</body></html>");
else if (uri.path().equals("/hello")) {
String name = Util.getOrElse(uri.parameter("name"), "Mister X");
connection.handleWithSyncHandler(requestHandler, materializer);
// this is equivalent to
//connection.handleWith(Flow.of(HttpRequest.class).map(requestHandler), materializer);
return
HttpResponse.create()
.withEntity("Hello " + name + "!");
}
})).run(materializer);
//#full-server-example
else if (uri.path().equals("/ping"))
return HttpResponse.create().withEntity("PONG!");
else
return NOT_FOUND;
}
else return NOT_FOUND;
}
};
//#request-handler
Future<ServerBinding> serverBindingFuture =
serverSource.to(Sink.foreach(
new Procedure<IncomingConnection>() {
@Override
public void apply(IncomingConnection connection) throws Exception {
System.out.println("Accepted new connection from " + connection.remoteAddress());
connection.handleWithSyncHandler(requestHandler, materializer);
// this is equivalent to
//connection.handleWith(Flow.of(HttpRequest.class).map(requestHandler), materializer);
}
})).run(materializer);
//#full-server-example
Await.result(serverBindingFuture, new FiniteDuration(1, TimeUnit.SECONDS)); // will throw if binding fails
System.out.println("Press ENTER to stop.");
new BufferedReader(new InputStreamReader(System.in)).readLine();
} finally {
system.shutdown();
}
}
public static void main(String[] args) throws Exception {
fullServerExample();
}
}

View file

@ -2,3 +2,7 @@
Connection-Level Client-Side API
================================
TODO
For the time being, :ref:`see the Scala chapter on the same topic <ConnectionLevelApi>`.

View file

@ -2,3 +2,7 @@
Host-Level Client-Side API
==========================
TODO
For the time being, :ref:`see the Scala chapter on the same topic <HostLevelApi>`.

View file

@ -2,3 +2,7 @@
Client-Side HTTPS Support
=========================
TODO
For the time being, :ref:`see the Scala chapter on the same topic <clientSideHTTPS>`.

View file

@ -3,4 +3,6 @@
Request-Level Client-Side API
=============================
...
TODO
For the time being, :ref:`see the Scala chapter on the same topic <RequestLevelApi>`.

View file

@ -1,4 +1,8 @@
.. _client-side-websocket-support-java:
Client-Side WebSocket Support
=============================
TODO
Not yet implemented see 17275_.
.. _17275: https://github.com/akka/akka/issues/17275

View file

@ -9,6 +9,7 @@ Akka HTTP
introduction
configuration
http-model
low-level-server-side-api
server-side/low-level-server-side-api
server-side/websocket-support
routing-dsl/index
client-side/index

View file

@ -8,7 +8,7 @@ with a browser is of course also in scope it is not the primary focus of Akka HT
Akka HTTP follows a rather open design and many times offers several different API levels for "doing the same thing".
You get to pick the API level of abstraction that is most suitable for your application.
This means that, if you have trouble achieving something using a high-level API, there's a good chance that you can get
it done with a low-level API, which offers more flexibility but might require you do write more application code.
it done with a low-level API, which offers more flexibility but might require you to write more application code.
Akka HTTP is structured into several modules:

View file

@ -0,0 +1,6 @@
.. _directives-java:
Directives
==========
TODO

View file

@ -8,4 +8,7 @@ High-level Server-Side API
.. toctree::
:maxdepth: 1
overview
overview
routes
directives/index
request-vals/index

View file

@ -1,4 +1,65 @@
.. _routing-java:
Routing DSL Overview
====================
...
The Akka HTTP :ref:`http-low-level-server-side-api-java` provides a ``Flow``- or ``Function``-level interface that allows
an application to respond to incoming HTTP requests by simply mapping requests to responses
(excerpt from :ref:`Low-level server side example <http-low-level-server-side-example-java>`):
.. includecode:: ../../code/docs/http/javadsl/HttpServerExampleSpec.java
:include: request-handler
While it'd be perfectly possible to define a complete REST API service purely by inspecting the incoming
``HttpRequest`` this approach becomes somewhat unwieldy for larger services due to the amount of syntax "ceremony"
required. Also, it doesn't help in keeping your service definition as DRY_ as you might like.
As an alternative Akka HTTP provides a flexible DSL for expressing your service behavior as a structure of
composable elements (called :ref:`directives-java`) in a concise and readable way. Directives are assembled into a so called
*route structure* which, at its top-level, can be used to create a handler ``Flow`` (or, alternatively, an
async handler function) that can be directly supplied to a ``bind`` call.
Here's the complete example rewritten using the composable high-level API:
.. includecode:: ../../code/docs/http/javadsl/HighLevelServerExample.java
:include: high-level-server-example
Heart of the high-level architecture is the route tree. It is a big expression of type ``Route``
that is evaluated only once during startup time of your service. It completely describes how your service
should react to any request.
The type ``Route`` is the basic building block of the route tree. It defines if and a how a request should
be handled. Routes are composed to form the route tree in the following two ways.
A route can be wrapped by a "Directive" which adds some behavioral aspect to its wrapped "inner route". ``path("ping")`` is such
a directive that implements a path filter, i.e. it only passes control to its inner route when the unmatched path
matches ``"ping"``. Directives can be more versatile than this: A directive can also transform the request before
passing it into its inner route or transform a response that comes out of its inner route. It's a general and powerful
abstraction that allows to package any kind of HTTP processing into well-defined blocks that can be freely combined.
akka-http defines a library of predefined directives and routes for all the various aspects of dealing with
HTTP requests and responses.
Read more about :ref:`directives-java`.
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 ``pathSingleSlash().route()`` or to directives that directly take a variable number
of inner routes as argument like ``get()`` here.
Read more about :ref:`routes-java`.
Another important building block is a ``RequestVal<T>``. It represents a value that can be extracted from a
request (like the URI parameter ``Parameters.stringValue("name")`` in the example) and which is then interpreted
as a value of type ``T``. Examples of HTTP aspects represented by a ``RequestVal`` are URI parameters, HTTP form
fields, details of the request like headers, URI, the entity, or authentication data.
Read more about :ref:`request-vals-java`.
Requests or responses often contain data that needs to be interpreted or rendered in some way.
Akka-http provides the abstraction of ``Marshaller`` and ``Unmarshaller`` that define how domain model objects map
to HTTP entities.
Read more about :ref:`marshalling-java`.
.. _DRY: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself

View file

@ -0,0 +1,37 @@
.. _request-vals-java:
Request values
==============
A request value of type ``RequestVal<T>`` is a typed structure that represents some aspect of the request
that can be interpreted as a value of type ``T``. A ``RequestVal`` instance abstracts the knowledge about how
to extract a certain value from the request and interpret it as a ``T``. It is used in combination with
:ref:`handlers-java`.
The advantage of representing a request detail as a ``RequestVal`` instead of performing ad-hoc analysis of
a request are:
* you can define an "inventory" of HTTP primitives for your application that you can reuse in many places of your
application
* automatic handling of errors when an expected value was not found in a request or if it could not be interpreted
as the expected Java type
Note, that the Scala version of the routing DSL has no direct correspondent to RequestVals. Instead,
a Scala-side ``Directive`` can have "extractions" that are reflected in the type of the ``Directive``.
Predefined Request values
-------------------------
akka-http provides a set of predefined request values for request data commonly accessed in a web
service.
These request values are defined:
* in ``RequestVals``: request values for basic data like URI components, request method, peer address, or the entity data
* in ``Cookies``: request values representing cookies
* in ``FormFields``: request values to access form fields unmarshalled to various primitive Java types
* in ``Headers``:: request values to access request headers or header values
* ``HttpBasicAuthenticator``: an abstract class to implement to create a request value representing a HTTP basic authenticated principal
* in ``Parameters``: request values to access URI paramaters unmarshalled to various primitive Java types
* in ``PathMatchers``: request values to match and access URI path segments
* ``CustomRequestVal``: an abstract class to implement arbitrary custom request values

View file

@ -0,0 +1,90 @@
.. _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 ``Future<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.

View file

@ -62,7 +62,7 @@ Starting and Stopping
On the most basic level an Akka HTTP server is bound by invoking the ``bind`` method of the `akka.http.javadsl.Http`_
extension:
.. includecode:: ../code/docs/http/javadsl/HttpServerExampleSpec.java
.. includecode:: ../../code/docs/http/javadsl/HttpServerExampleSpec.java
:include: binding-example
Arguments to the ``Http().bind`` method specify the interface and port to bind to and register interest in handling
@ -82,6 +82,7 @@ useful for example when binding to port zero (and thus letting the OS pick an av
.. _akka.http.javadsl.Http: @github@/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala
.. _http-low-level-server-side-example-java:
Request-Response Cycle
----------------------
@ -98,7 +99,7 @@ Requests are handled by calling one of the ``handleWithXXX`` methods with a hand
Here is a complete example:
.. includecode:: ../code/docs/http/javadsl/HttpServerExampleSpec.java
.. includecode:: ../../code/docs/http/javadsl/HttpServerExampleSpec.java
:include: full-server-example
In this example, a request is handled by transforming the request stream with a function ``Function<HttpRequest, HttpResponse>``
@ -111,6 +112,7 @@ if HTTP pipelining is enabled where processing of multiple incoming requests may
``handleWithSyncHandler`` or ``handleWithAsyncHandler``, or the ``map`` or ``mapAsync`` stream operators, this
requirement will be automatically fulfilled.
See :ref:`routing-java` for a more convenient high-level DSL to create request handlers.
Streaming Request/Response Entities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -0,0 +1,8 @@
.. _server-side-websocket-support-java:
Server-Side WebSocket Support
=============================
TODO
For the time being, :ref:`see the Scala chapter on the same topic <server-side-websocket-support-scala>`.