diff --git a/akka-docs-dev/rst/java/code/docs/http/javadsl/HighLevelServerExample.java b/akka-docs-dev/rst/java/code/docs/http/javadsl/HighLevelServerExample.java new file mode 100644 index 0000000000..4fd0e09052 --- /dev/null +++ b/akka-docs-dev/rst/java/code/docs/http/javadsl/HighLevelServerExample.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +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 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() { + @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(), + "Hello world!") + ), + path("ping").route( + // return a simple `text/plain` response + complete("PONG!") + ), + path("hello").route( + // uses the route defined above + helloRoute + ) + ) + ); + } +} +//#high-level-server-example \ No newline at end of file diff --git a/akka-docs-dev/rst/java/code/docs/http/javadsl/HttpServerExampleSpec.java b/akka-docs-dev/rst/java/code/docs/http/javadsl/HttpServerExampleSpec.java index e0bc5a2edc..ff0731baaa 100644 --- a/akka-docs-dev/rst/java/code/docs/http/javadsl/HttpServerExampleSpec.java +++ b/akka-docs-dev/rst/java/code/docs/http/javadsl/HttpServerExampleSpec.java @@ -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> serverSource = - Http.get(system).bind("localhost", 8080, materializer); + Source> serverSource = + Http.get(system).bind("localhost", 8080, materializer); - final Function requestHandler = - new Function() { - private final HttpResponse NOT_FOUND = - HttpResponse.create() - .withStatus(404) - .withEntity("Unknown resource!"); + //#request-handler + final Function requestHandler = + new Function() { + 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(), - "Hello world!"); - else if (request.getUri().path().equals("/ping")) - return HttpResponse.create().withEntity("PONG!"); - else - return NOT_FOUND; - } - else return NOT_FOUND; - } - }; - Future serverBindingFuture = - serverSource.to(Sink.foreach( - new Procedure() { - @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(), + "Hello world!"); + 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 serverBindingFuture = + serverSource.to(Sink.foreach( + new Procedure() { + @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(); } } diff --git a/akka-docs-dev/rst/java/http/client-side/connection-level.rst b/akka-docs-dev/rst/java/http/client-side/connection-level.rst index 8c68877973..fc41a2d08f 100644 --- a/akka-docs-dev/rst/java/http/client-side/connection-level.rst +++ b/akka-docs-dev/rst/java/http/client-side/connection-level.rst @@ -2,3 +2,7 @@ Connection-Level Client-Side API ================================ + +TODO + +For the time being, :ref:`see the Scala chapter on the same topic `. \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/client-side/host-level.rst b/akka-docs-dev/rst/java/http/client-side/host-level.rst index 0484303b98..3efcf7ace1 100644 --- a/akka-docs-dev/rst/java/http/client-side/host-level.rst +++ b/akka-docs-dev/rst/java/http/client-side/host-level.rst @@ -2,3 +2,7 @@ Host-Level Client-Side API ========================== + +TODO + +For the time being, :ref:`see the Scala chapter on the same topic `. \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/client-side/https-support.rst b/akka-docs-dev/rst/java/http/client-side/https-support.rst index f97420cba2..c9175c080d 100644 --- a/akka-docs-dev/rst/java/http/client-side/https-support.rst +++ b/akka-docs-dev/rst/java/http/client-side/https-support.rst @@ -2,3 +2,7 @@ Client-Side HTTPS Support ========================= + +TODO + +For the time being, :ref:`see the Scala chapter on the same topic `. \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/client-side/request-level.rst b/akka-docs-dev/rst/java/http/client-side/request-level.rst index 75eb9b4e62..7941d7dc40 100644 --- a/akka-docs-dev/rst/java/http/client-side/request-level.rst +++ b/akka-docs-dev/rst/java/http/client-side/request-level.rst @@ -3,4 +3,6 @@ Request-Level Client-Side API ============================= -... \ No newline at end of file +TODO + +For the time being, :ref:`see the Scala chapter on the same topic `. \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/client-side/websocket-support.rst b/akka-docs-dev/rst/java/http/client-side/websocket-support.rst index 69cdb560eb..91d0d0aa1c 100644 --- a/akka-docs-dev/rst/java/http/client-side/websocket-support.rst +++ b/akka-docs-dev/rst/java/http/client-side/websocket-support.rst @@ -1,4 +1,8 @@ +.. _client-side-websocket-support-java: + Client-Side WebSocket Support ============================= -TODO \ No newline at end of file +Not yet implemented see 17275_. + +.. _17275: https://github.com/akka/akka/issues/17275 \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/index.rst b/akka-docs-dev/rst/java/http/index.rst index ccaa676527..a3547ea710 100644 --- a/akka-docs-dev/rst/java/http/index.rst +++ b/akka-docs-dev/rst/java/http/index.rst @@ -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 \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/introduction.rst b/akka-docs-dev/rst/java/http/introduction.rst index fe641ea0a6..d7080f2a1c 100644 --- a/akka-docs-dev/rst/java/http/introduction.rst +++ b/akka-docs-dev/rst/java/http/introduction.rst @@ -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: diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/index.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/index.rst new file mode 100644 index 0000000000..c72ee6c145 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/index.rst @@ -0,0 +1,6 @@ +.. _directives-java: + +Directives +========== + +TODO \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/routing-dsl/index.rst b/akka-docs-dev/rst/java/http/routing-dsl/index.rst index 85758fd319..24a841c0c7 100644 --- a/akka-docs-dev/rst/java/http/routing-dsl/index.rst +++ b/akka-docs-dev/rst/java/http/routing-dsl/index.rst @@ -8,4 +8,7 @@ High-level Server-Side API .. toctree:: :maxdepth: 1 - overview \ No newline at end of file + overview + routes + directives/index + request-vals/index diff --git a/akka-docs-dev/rst/java/http/routing-dsl/overview.rst b/akka-docs-dev/rst/java/http/routing-dsl/overview.rst index 477c5fb3ec..42979b6d54 100644 --- a/akka-docs-dev/rst/java/http/routing-dsl/overview.rst +++ b/akka-docs-dev/rst/java/http/routing-dsl/overview.rst @@ -1,4 +1,65 @@ +.. _routing-java: + Routing DSL Overview ==================== -... \ No newline at end of file +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 `): + +.. 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``. 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 diff --git a/akka-docs-dev/rst/java/http/routing-dsl/request-vals/index.rst b/akka-docs-dev/rst/java/http/routing-dsl/request-vals/index.rst new file mode 100644 index 0000000000..678db867e3 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/request-vals/index.rst @@ -0,0 +1,37 @@ +.. _request-vals-java: + +Request values +============== + +A request value of type ``RequestVal`` 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 diff --git a/akka-docs-dev/rst/java/http/routing-dsl/routes.rst b/akka-docs-dev/rst/java/http/routing-dsl/routes.rst new file mode 100644 index 0000000000..84dcbc86f6 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/routes.rst @@ -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`` @@ -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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/akka-docs-dev/rst/java/http/server-side/websocket-support.rst b/akka-docs-dev/rst/java/http/server-side/websocket-support.rst new file mode 100644 index 0000000000..898bc9dd6e --- /dev/null +++ b/akka-docs-dev/rst/java/http/server-side/websocket-support.rst @@ -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 `. \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/client-side/websocket-support.rst b/akka-docs-dev/rst/scala/http/client-side/websocket-support.rst index 69cdb560eb..522a8123c7 100644 --- a/akka-docs-dev/rst/scala/http/client-side/websocket-support.rst +++ b/akka-docs-dev/rst/scala/http/client-side/websocket-support.rst @@ -1,4 +1,8 @@ +.. _client-side-websocket-support: + Client-Side WebSocket Support ============================= -TODO \ No newline at end of file +Not yet implemented see 17275_. + +.. _17275: https://github.com/akka/akka/issues/17275 \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/introduction.rst b/akka-docs-dev/rst/scala/http/introduction.rst index 33e92e62e7..c21cffbbbf 100644 --- a/akka-docs-dev/rst/scala/http/introduction.rst +++ b/akka-docs-dev/rst/scala/http/introduction.rst @@ -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: diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/websocket-support.rst b/akka-docs-dev/rst/scala/http/routing-dsl/websocket-support.rst index 3dced95dde..33cd90cb04 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/websocket-support.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/websocket-support.rst @@ -1,3 +1,5 @@ +.. _server-side-websocket-support-scala: + Server-Side WebSocket Support ============================= diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/BasicDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/BasicDirectives.scala index 04b66d51a5..d8589b2dc0 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/BasicDirectives.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/directives/BasicDirectives.scala @@ -7,7 +7,7 @@ package akka.http.javadsl.server.directives import scala.annotation.varargs import java.lang.reflect.{ ParameterizedType, Method } -import akka.http.javadsl.model.{ StatusCode, HttpResponse } +import akka.http.javadsl.model.{ ContentType, StatusCode, HttpResponse } import akka.http.javadsl.server._ import akka.http.impl.server.RouteStructure._ import akka.http.impl.server._ @@ -30,6 +30,15 @@ abstract class BasicDirectives { def handle(ctx: RequestContext): RouteResult = ctx.complete(text) } + /** + * A route that completes the request with a static text + */ + def complete(contentType: ContentType, text: String): Route = + new OpaqueRoute() { + def handle(ctx: RequestContext): RouteResult = + ctx.complete(contentType, text) + } + /** * A route that completes the request with a static text */