Merge pull request #17990 from spray/w/java-side-documentation
A few last minute Java-side changes + documentation
This commit is contained in:
commit
75229b0711
24 changed files with 829 additions and 98 deletions
|
|
@ -63,8 +63,8 @@ public class HandlerExampleSpec extends JUnitRouteTest {
|
|||
RequestVal<Integer> xParam = Parameters.intValue("x");
|
||||
RequestVal<Integer> yParam = Parameters.intValue("y");
|
||||
|
||||
RequestVal<Integer> xSegment = PathMatchers.integerNumber();
|
||||
RequestVal<Integer> ySegment = PathMatchers.integerNumber();
|
||||
RequestVal<Integer> xSegment = PathMatchers.intValue();
|
||||
RequestVal<Integer> ySegment = PathMatchers.intValue();
|
||||
|
||||
//#handler2
|
||||
Handler2<Integer, Integer> multiply =
|
||||
|
|
@ -114,8 +114,8 @@ public class HandlerExampleSpec extends JUnitRouteTest {
|
|||
RequestVal<Integer> xParam = Parameters.intValue("x");
|
||||
RequestVal<Integer> yParam = Parameters.intValue("y");
|
||||
|
||||
RequestVal<Integer> xSegment = PathMatchers.integerNumber();
|
||||
RequestVal<Integer> ySegment = PathMatchers.integerNumber();
|
||||
RequestVal<Integer> xSegment = PathMatchers.intValue();
|
||||
RequestVal<Integer> ySegment = PathMatchers.intValue();
|
||||
|
||||
|
||||
//#reflective
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.javadsl;
|
||||
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.model.StatusCodes;
|
||||
import akka.http.javadsl.server.*;
|
||||
import akka.http.javadsl.server.values.Parameters;
|
||||
import akka.http.javadsl.server.values.PathMatcher;
|
||||
import akka.http.javadsl.server.values.PathMatchers;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
import akka.http.javadsl.testkit.TestRoute;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PathDirectiveExampleTest extends JUnitRouteTest {
|
||||
@Test
|
||||
public void testPathPrefix() {
|
||||
//#path-examples
|
||||
// matches "/test"
|
||||
path("test").route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
);
|
||||
|
||||
// matches "/test", as well
|
||||
path(PathMatchers.segment("test")).route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
);
|
||||
|
||||
// matches "/admin/user"
|
||||
path("admin", "user").route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
);
|
||||
|
||||
// matches "/admin/user", as well
|
||||
pathPrefix("admin").route(
|
||||
path("user").route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
)
|
||||
);
|
||||
|
||||
// matches "/admin/user/<user-id>"
|
||||
Handler1<Integer> completeWithUserId =
|
||||
new Handler1<Integer>() {
|
||||
@Override
|
||||
public RouteResult handle(RequestContext ctx, Integer userId) {
|
||||
return ctx.complete("Hello user " + userId);
|
||||
}
|
||||
};
|
||||
PathMatcher<Integer> userId = PathMatchers.intValue();
|
||||
pathPrefix("admin", "user").route(
|
||||
path(userId).route(
|
||||
handleWith1(userId, completeWithUserId)
|
||||
)
|
||||
);
|
||||
|
||||
// matches "/admin/user/<user-id>", as well
|
||||
path("admin", "user", userId).route(
|
||||
handleWith1(userId, completeWithUserId)
|
||||
);
|
||||
|
||||
// never matches
|
||||
path("admin").route( // oops this only matches "/admin"
|
||||
path("user").route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
)
|
||||
);
|
||||
|
||||
// matches "/user/" with the first subroute, "/user" (without a trailing slash)
|
||||
// with the second subroute, and "/user/<user-id>" with the last one.
|
||||
pathPrefix("user").route(
|
||||
pathSingleSlash().route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
),
|
||||
pathEnd().route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
),
|
||||
path(userId).route(
|
||||
handleWith1(userId, completeWithUserId)
|
||||
)
|
||||
);
|
||||
//#path-examples
|
||||
}
|
||||
|
||||
// FIXME: remove once #17988 is merged
|
||||
public static <T> Route handleWith1(RequestVal<T> val, Object o) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,13 +3,39 @@
|
|||
Akka HTTP
|
||||
=========
|
||||
|
||||
The Akka HTTP modules implement a full server- and client-side HTTP stack on top of *akka-actor* and *akka-stream*. It's
|
||||
not a web-framework but rather a more general toolkit for providing and consuming HTTP-based services. While interaction
|
||||
with a browser is of course also in scope it is not the primary focus of Akka HTTP.
|
||||
|
||||
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 to write more application code.
|
||||
|
||||
Akka HTTP is structured into several modules:
|
||||
|
||||
akka-http-core
|
||||
A complete, mostly low-level, server- and client-side implementation of HTTP (incl. WebSockets).
|
||||
Includes a model of all things HTTP.
|
||||
|
||||
akka-http
|
||||
Higher-level functionality, like (un)marshalling, (de)compression as well as a powerful DSL
|
||||
for defining HTTP-based APIs on the server-side
|
||||
|
||||
akka-http-testkit
|
||||
A test harness and set of utilities for verifying server-side service implementations
|
||||
|
||||
akka-http-jackson
|
||||
Predefined glue-code for (de)serializing custom types from/to JSON with jackson_
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
introduction
|
||||
configuration
|
||||
http-model
|
||||
server-side/low-level-server-side-api
|
||||
server-side/websocket-support
|
||||
routing-dsl/index
|
||||
client-side/index
|
||||
|
||||
.. _jackson: https://github.com/FasterXML/jackson
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
Introduction
|
||||
============
|
||||
|
||||
The Akka HTTP modules implement a full server- and client-side HTTP stack on top of *akka-actor* and *akka-stream*. It's
|
||||
not a web-framework but rather a more general toolkit for providing and consuming HTTP-based services. While interaction
|
||||
with a browser is of course also in scope it is not the primary focus of Akka HTTP.
|
||||
|
||||
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 to write more application code.
|
||||
|
||||
Akka HTTP is structured into several modules:
|
||||
|
||||
akka-http-core
|
||||
A complete, mostly low-level, server- and client-side implementation of HTTP (incl. WebSockets)
|
||||
|
||||
akka-http
|
||||
Higher-level functionality, like (un)marshalling, (de)compression as well as a powerful DSL
|
||||
for defining HTTP-based APIs on the server-side
|
||||
|
||||
akka-http-testkit
|
||||
A test harness and set of utilities for verifying server-side service implementations
|
||||
|
||||
akka-http-jackson
|
||||
Predefined glue-code for (de)serializing custom types from/to JSON with jackson_
|
||||
|
||||
.. _jackson: https://github.com/FasterXML/jackson
|
||||
|
|
@ -3,4 +3,64 @@
|
|||
Directives
|
||||
==========
|
||||
|
||||
TODO
|
||||
A directive is a wrapper for a route or a list of alternative routes that adds one or more of the following
|
||||
functionality to its nested route(s):
|
||||
|
||||
* it filters the request and lets only matching requests pass (e.g. the `get` directive lets only GET-requests pass)
|
||||
* it modifies the request or the ``RequestContext`` (e.g. the `path` directives filters on the unmatched path and then
|
||||
passes an updated ``RequestContext`` unmatched path)
|
||||
* it modifies the response coming out of the nested route
|
||||
|
||||
akka-http provides a set of predefined directives for various tasks. You can access them by either extending from
|
||||
``akka.http.javadsl.server.AllDirectives`` or by importing them statically with
|
||||
``import static akka.http.javadsl.server.Directives.*;``.
|
||||
|
||||
These classes of directives are currently defined:
|
||||
|
||||
BasicDirectives
|
||||
Contains methods to create routes that complete with a static values or allow specifying :ref:`handlers-java` to
|
||||
process a request.
|
||||
|
||||
CacheConditionDirectives
|
||||
Contains a single directive ``conditional`` that wraps its inner route with support for Conditional Requests as defined
|
||||
by `RFC 7234`_.
|
||||
|
||||
CodingDirectives
|
||||
Contains directives to decode compressed requests and encode responses.
|
||||
|
||||
CookieDirectives
|
||||
Contains a single directive ``setCookie`` to aid adding a cookie to a response.
|
||||
|
||||
ExecutionDirectives
|
||||
Contains directives to deal with exceptions that occurred during routing.
|
||||
|
||||
FileAndResourceDirectives
|
||||
Contains directives to serve resources from files on the file system or from the classpath.
|
||||
|
||||
HostDirectives
|
||||
Contains directives to filter on the ``Host`` header of the incoming request.
|
||||
|
||||
MethodDirectives
|
||||
Contains directives to filter on the HTTP method of the incoming request.
|
||||
|
||||
MiscDirectives
|
||||
Contains directives that validate a request by user-defined logic.
|
||||
|
||||
:ref:`PathDirectives-java`
|
||||
Contains directives to match and filter on the URI path of the incoming request.
|
||||
|
||||
RangeDirectives
|
||||
Contains a single directive ``withRangeSupport`` that adds support for retrieving partial responses.
|
||||
|
||||
SchemeDirectives
|
||||
Contains a single directive ``scheme`` to filter requests based on the URI scheme (http vs. https).
|
||||
|
||||
WebsocketDirectives
|
||||
Contains directives to support answering Websocket requests.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
path-directives
|
||||
|
||||
.. _`RFC 7234`: http://tools.ietf.org/html/rfc7234
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
.. _PathDirectives-java:
|
||||
|
||||
PathDirectives
|
||||
==============
|
||||
|
||||
Path directives are the most basic building blocks for routing requests depending on the URI path.
|
||||
|
||||
When a request (or rather the respective ``RequestContext`` instance) enters the route structure it has an
|
||||
"unmatched path" that is identical to the ``request.uri.path``. As it descends the routing tree and passes through one
|
||||
or more ``pathPrefix`` or ``path`` directives the "unmatched path" progressively gets "eaten into" from the
|
||||
left until, in most cases, it eventually has been consumed completely.
|
||||
|
||||
The two main directives are ``path`` and ``pathPrefix``. The ``path`` directive tries to match the complete remaining
|
||||
unmatched path against the specified "path matchers", the ``pathPrefix`` directive only matches a prefix and passes the
|
||||
remaining unmatched path to nested directives. Both directives automatically match a slash from the beginning, so
|
||||
that matching slashes in a hierarchy of nested ``pathPrefix`` and ``path`` directives is usually not needed.
|
||||
|
||||
Path directives take a variable amount of arguments. Each argument must be a ``PathMatcher`` or a string (which is
|
||||
automatically converted to a path matcher using ``PathMatchers.segment``). In the case of ``path`` and ``pathPrefix``,
|
||||
if multiple arguments are supplied, a slash is assumed between any of the supplied path matchers. The ``rawPathX``
|
||||
variants of those directives on the other side do no such preprocessing, so that slashes must be matched manually.
|
||||
|
||||
Path Matchers
|
||||
-------------
|
||||
|
||||
A path matcher is a description of a part of a path to match. The simplest path matcher is ``PathMatcher.segment`` which
|
||||
matches exactly one path segment against the supplied constant string.
|
||||
|
||||
Other path matchers defined in ``PathMatchers`` match the end of the path (``PathMatchers.END``), a single slash
|
||||
(``PathMatchers.SLASH``), or nothing at all (``PathMatchers.NEUTRAL``).
|
||||
|
||||
Many path matchers are hybrids that can both match (by using them with one of the PathDirectives) and extract values,
|
||||
i.e. they are :ref:`request-vals-java`. Extracting a path matcher value (i.e. using it with ``handleWithX``) is only
|
||||
allowed if it nested inside a path directive that uses that path matcher and so specifies at which position the value
|
||||
should be extracted from the path.
|
||||
|
||||
Predefined path matchers allow extraction of various types of values:
|
||||
|
||||
``PathMatchers.segment(String)``
|
||||
Strings simply match themselves and extract no value.
|
||||
Note that strings are interpreted as the decoded representation of the path, so if they include a '/' character
|
||||
this character will match "%2F" in the encoded raw URI!
|
||||
|
||||
``PathMatchers.regex``
|
||||
You can use a regular expression instance as a path matcher, which matches whatever the regex matches and extracts
|
||||
one ``String`` value. A ``PathMatcher`` created from a regular expression extracts either the complete match (if the
|
||||
regex doesn't contain a capture group) or the capture group (if the regex contains exactly one capture group).
|
||||
If the regex contains more than one capture group an ``IllegalArgumentException`` will be thrown.
|
||||
|
||||
``PathMatchers.SLASH``
|
||||
Matches exactly one path-separating slash (``/``) character.
|
||||
|
||||
``PathMatchers.END``
|
||||
Matches the very end of the path, similar to ``$`` in regular expressions.
|
||||
|
||||
``PathMatchers.Segment``
|
||||
Matches if the unmatched path starts with a path segment (i.e. not a slash).
|
||||
If so the path segment is extracted as a ``String`` instance.
|
||||
|
||||
``PathMatchers.Rest``
|
||||
Matches and extracts the complete remaining unmatched part of the request's URI path as an (encoded!) String.
|
||||
If you need access to the remaining *decoded* elements of the path use ``RestPath`` instead.
|
||||
|
||||
``PathMatchers.intValue``
|
||||
Efficiently matches a number of decimal digits (unsigned) and extracts their (non-negative) ``Int`` value. The matcher
|
||||
will not match zero digits or a sequence of digits that would represent an ``Int`` value larger than ``Integer.MAX_VALUE``.
|
||||
|
||||
``PathMatchers.longValue``
|
||||
Efficiently matches a number of decimal digits (unsigned) and extracts their (non-negative) ``Long`` value. The matcher
|
||||
will not match zero digits or a sequence of digits that would represent an ``Long`` value larger than ``Long.MAX_VALUE``.
|
||||
|
||||
``PathMatchers.hexIntValue``
|
||||
Efficiently matches a number of hex digits and extracts their (non-negative) ``Int`` value. The matcher will not match
|
||||
zero digits or a sequence of digits that would represent an ``Int`` value larger than ``Integer.MAX_VALUE``.
|
||||
|
||||
``PathMatchers.hexLongValue``
|
||||
Efficiently matches a number of hex digits and extracts their (non-negative) ``Long`` value. The matcher will not
|
||||
match zero digits or a sequence of digits that would represent an ``Long`` value larger than ``Long.MAX_VALUE``.
|
||||
|
||||
``PathMatchers.uuid``
|
||||
Matches and extracts a ``java.util.UUID`` instance.
|
||||
|
||||
``PathMatchers.NEUTRAL``
|
||||
A matcher that always matches, doesn't consume anything and extracts nothing.
|
||||
Serves mainly as a neutral element in ``PathMatcher`` composition.
|
||||
|
||||
``PathMatchers.segments``
|
||||
Matches all remaining segments as a list of strings. Note that this can also be "no segments" resulting in the empty
|
||||
list. If the path has a trailing slash this slash will *not* be matched, i.e. remain unmatched and to be consumed by
|
||||
potentially nested directives.
|
||||
|
||||
Here's a collection of path matching examples:
|
||||
|
||||
.. includecode:: ../../../code/docs/http/javadsl/PathDirectiveExampleTest.java
|
||||
:include: path-examples
|
||||
|
|
@ -59,7 +59,7 @@ route structure, this time representing segments from the URI path.
|
|||
Handlers in Java 8
|
||||
------------------
|
||||
|
||||
In Java 8 handlers can be provided as function literals. The example from before then looks like this:
|
||||
In Java 8 handlers can be provided as function literals. The previous example can then be written like this:
|
||||
|
||||
.. includecode:: /../../akka-http-tests-java8/src/test/java/docs/http/javadsl/server/HandlerExampleSpec.java
|
||||
:include: handler2-example-full
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
High-level Server-Side API
|
||||
==========================
|
||||
|
||||
...
|
||||
To use the high-level API you need to add a dependency to the ``akka-http-experimental`` module.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
|
@ -13,5 +13,5 @@ High-level Server-Side API
|
|||
directives/index
|
||||
request-vals/index
|
||||
handlers
|
||||
path-matchers
|
||||
marshalling
|
||||
json-jackson-support
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
.. _json-jackson-support-java:
|
||||
|
||||
Json Support via Jackson
|
||||
========================
|
||||
|
||||
akka-http provides support to convert application-domain objects from and to JSON using jackson_. To make use
|
||||
of the support module, you need to add a dependency on `akka-http-jackson-experimental`.
|
||||
|
||||
Use ``akka.http.javadsl.marshallers.jackson.Jackson.jsonAs[T]`` to create a ``RequestVal<T>`` which expects the request
|
||||
body to be of type ``application/json`` and converts it to ``T`` using Jackson.
|
||||
|
||||
See `this example`__ in the sources for an example.
|
||||
|
||||
Use ``akka.http.javadsl.marshallers.jackson.Jackson.json[T]`` to create a ``Marshaller<T>`` which can be used with
|
||||
``RequestContext.completeAs`` to convert a POJO to an HttpResponse.
|
||||
|
||||
|
||||
.. _jackson: https://github.com/FasterXML/jackson
|
||||
__ @github@/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/petstore/PetStoreExample.java
|
||||
|
|
@ -3,4 +3,41 @@
|
|||
Marshalling & Unmarshalling
|
||||
===========================
|
||||
|
||||
TODO
|
||||
"Marshalling" is the process of converting a higher-level (object) structure into some kind of lower-level
|
||||
representation (and vice versa), often a binary wire format. Other popular names for it are "Serialization" or
|
||||
"Pickling".
|
||||
|
||||
In akka-http "Marshalling" means the conversion of an object of type T into an HttpEntity, which forms the entity body
|
||||
of an HTTP request or response (depending on whether used on the client or server side).
|
||||
|
||||
Marshalling
|
||||
-----------
|
||||
|
||||
On the server-side marshalling is used to convert a application-domain object to a response (entity). Requests can
|
||||
contain an ``Accept`` header that lists acceptable content types for the client. A marshaller contains the logic to
|
||||
negotiate the result content types based on the ``Accept`` and the ``AcceptCharset`` headers.
|
||||
|
||||
Marshallers can be specified when completing a request with ``RequestContext.completeAs`` or by using the ``BasicDirectives.completeAs``
|
||||
directives.
|
||||
|
||||
These marshallers are provided by akka-http:
|
||||
|
||||
* Use :ref:`json-jackson-support-java` to create an marshaller that can convert a POJO to an ``application/json``
|
||||
response using jackson_.
|
||||
* Use ``Marshallers.toEntityString``, ``Marshallers.toEntityBytes``, ``Marshallers.toEntityByteString``,
|
||||
``Marshallers.toEntity``, and ``Marshallers.toResponse`` to create custom marshallers.
|
||||
|
||||
Unmarshalling
|
||||
-------------
|
||||
|
||||
On the server-side unmarshalling is used to convert a request (entity) to a application-domain object. This means
|
||||
unmarshalling to a certain type is represented by a ``RequestVal``. Currently, several options are provided to create
|
||||
an unmarshalling ``RequestVal``:
|
||||
|
||||
* Use :ref:`json-jackson-support-java` to create an unmarshaller that can convert an ``application/json`` request
|
||||
to a POJO using jackson_.
|
||||
* Use the predefined ``Unmarshallers.String``, ``Unmarshallers.ByteString``, ``Unmarshallers.ByteArray``,
|
||||
``Unmarshallers.CharArray`` to convert to those basic types.
|
||||
* Use ``Unmarshallers.fromMessage`` or ``Unmarshaller.fromEntity`` to create a custom unmarshaller.
|
||||
|
||||
.. _jackson: https://github.com/FasterXML/jackson
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
.. _pathmatcher-dsl-java:
|
||||
|
||||
The PathMatcher DSL
|
||||
===================
|
||||
|
||||
TODO
|
||||
|
|
@ -27,11 +27,19 @@ 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
|
||||
RequestVals
|
||||
Contains request values for basic data like URI components, request method, peer address, or the entity data.
|
||||
Cookies
|
||||
Contains request values representing cookies.
|
||||
FormFields
|
||||
Contains request values to access form fields unmarshalled to various primitive Java types.
|
||||
Headers
|
||||
Contains 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.
|
||||
Parameters
|
||||
Contains request values to access URI paramaters unmarshalled to various primitive Java types.
|
||||
PathMatchers
|
||||
Contains request values to match and access URI path segments.
|
||||
CustomRequestVal
|
||||
An abstract class to implement arbitrary custom request values.
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ public class SimpleServerApp8 extends HttpApp {
|
|||
static Parameter<Integer> x = Parameters.intValue("x");
|
||||
static Parameter<Integer> y = Parameters.intValue("y");
|
||||
|
||||
static PathMatcher<Integer> xSegment = PathMatchers.integerNumber();
|
||||
static PathMatcher<Integer> ySegment = PathMatchers.integerNumber();
|
||||
static PathMatcher<Integer> xSegment = PathMatchers.intValue();
|
||||
static PathMatcher<Integer> ySegment = PathMatchers.intValue();
|
||||
|
||||
public static RouteResult multiply(RequestContext ctx, int x, int y) {
|
||||
int result = x * y;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import static akka.http.javadsl.server.Directives.*;
|
||||
|
||||
public class PetStoreExample {
|
||||
static PathMatcher<Integer> petId = PathMatchers.integerNumber();
|
||||
static PathMatcher<Integer> petId = PathMatchers.intValue();
|
||||
static RequestVal<Pet> petEntity = RequestVals.entityAs(Jackson.jsonAs(Pet.class));
|
||||
|
||||
public static Route appRoute(final Map<Integer, Pet> pets) {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ public class SimpleServerApp extends HttpApp {
|
|||
static Parameter<Integer> x = Parameters.intValue("x");
|
||||
static Parameter<Integer> y = Parameters.intValue("y");
|
||||
|
||||
static PathMatcher<Integer> xSegment = PathMatchers.integerNumber();
|
||||
static PathMatcher<Integer> ySegment = PathMatchers.integerNumber();
|
||||
static PathMatcher<Integer> xSegment = PathMatchers.intValue();
|
||||
static PathMatcher<Integer> ySegment = PathMatchers.intValue();
|
||||
|
||||
static RequestVal<String> bodyAsName = RequestVals.entityAs(Unmarshallers.String());
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.javadsl.server;
|
||||
|
||||
import akka.http.javadsl.model.*;
|
||||
import akka.http.javadsl.model.headers.Accept;
|
||||
import akka.http.javadsl.model.headers.AcceptCharset;
|
||||
import akka.http.javadsl.server.values.Parameters;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
import akka.http.javadsl.testkit.TestRoute;
|
||||
import akka.japi.function.Function;
|
||||
import akka.util.ByteString;
|
||||
import org.junit.Test;
|
||||
|
||||
public class MarshallerTest extends JUnitRouteTest {
|
||||
RequestVal<Integer> n = Parameters.intValue("n");
|
||||
|
||||
@Test
|
||||
public void testCustomToStringMarshaller() {
|
||||
final Marshaller<Integer> numberAsNameMarshaller =
|
||||
Marshallers.toEntityString(MediaTypes.TEXT_X_SPEECH, new Function<Integer, String>() {
|
||||
@Override
|
||||
public String apply(Integer param) throws Exception {
|
||||
switch(param) {
|
||||
case 0: return "null";
|
||||
case 1: return "eins";
|
||||
case 2: return "zwei";
|
||||
case 3: return "drei";
|
||||
case 4: return "vier";
|
||||
case 5: return "fünf";
|
||||
default: return "wat?";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Handler1<Integer> nummerHandler = new Handler1<Integer>() {
|
||||
@Override
|
||||
public RouteResult handle(RequestContext ctx, Integer integer) {
|
||||
return ctx.completeAs(numberAsNameMarshaller, integer);
|
||||
}
|
||||
};
|
||||
|
||||
TestRoute route =
|
||||
testRoute(
|
||||
get(
|
||||
path("nummer").route(
|
||||
handleWith(n, nummerHandler)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
route.run(HttpRequest.GET("/nummer?n=1"))
|
||||
.assertStatusCode(200)
|
||||
.assertMediaType(MediaTypes.TEXT_X_SPEECH)
|
||||
.assertEntity("eins");
|
||||
|
||||
route.run(HttpRequest.GET("/nummer?n=6"))
|
||||
.assertStatusCode(200)
|
||||
.assertMediaType(MediaTypes.TEXT_X_SPEECH)
|
||||
.assertEntity("wat?");
|
||||
|
||||
route.run(HttpRequest.GET("/nummer?n=5"))
|
||||
.assertStatusCode(200)
|
||||
.assertEntityBytes(ByteString.fromString("fünf", "utf8"));
|
||||
|
||||
route.run(
|
||||
HttpRequest.GET("/nummer?n=5")
|
||||
.addHeader(AcceptCharset.create(HttpCharsets.ISO_8859_1.toRange())))
|
||||
.assertStatusCode(200)
|
||||
.assertEntityBytes(ByteString.fromString("fünf", "ISO-8859-1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomToByteStringMarshaller() {
|
||||
final Marshaller<Integer> numberAsJsonListMarshaller =
|
||||
Marshallers.toEntityByteString(MediaTypes.APPLICATION_JSON.toContentType(), new Function<Integer, ByteString>() {
|
||||
@Override
|
||||
public ByteString apply(Integer param) throws Exception {
|
||||
switch(param) {
|
||||
case 1: return ByteString.fromString("[1]");
|
||||
case 5: return ByteString.fromString("[1,2,3,4,5]");
|
||||
default: return ByteString.fromString("[]");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Handler1<Integer> nummerHandler = new Handler1<Integer>() {
|
||||
@Override
|
||||
public RouteResult handle(RequestContext ctx, Integer integer) {
|
||||
return ctx.completeAs(numberAsJsonListMarshaller, integer);
|
||||
}
|
||||
};
|
||||
|
||||
TestRoute route =
|
||||
testRoute(
|
||||
get(
|
||||
path("nummer").route(
|
||||
handleWith(n, nummerHandler)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
route.run(HttpRequest.GET("/nummer?n=1"))
|
||||
.assertStatusCode(200)
|
||||
.assertMediaType(MediaTypes.APPLICATION_JSON)
|
||||
.assertEntity("[1]");
|
||||
|
||||
route.run(HttpRequest.GET("/nummer?n=5"))
|
||||
.assertStatusCode(200)
|
||||
.assertEntity("[1,2,3,4,5]");
|
||||
|
||||
route.run(
|
||||
HttpRequest.GET("/nummer?n=5").addHeader(Accept.create(MediaTypes.TEXT_PLAIN.toRange())))
|
||||
.assertStatusCode(406);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomToEntityMarshaller() {
|
||||
final Marshaller<Integer> numberAsJsonListMarshaller =
|
||||
Marshallers.toEntity(MediaTypes.APPLICATION_JSON.toContentType(), new Function<Integer, ResponseEntity>() {
|
||||
@Override
|
||||
public ResponseEntity apply(Integer param) throws Exception {
|
||||
switch(param) {
|
||||
case 1: return HttpEntities.create(MediaTypes.APPLICATION_JSON.toContentType(), "[1]");
|
||||
case 5: return HttpEntities.create(MediaTypes.APPLICATION_JSON.toContentType(), "[1,2,3,4,5]");
|
||||
default: return HttpEntities.create(MediaTypes.APPLICATION_JSON.toContentType(), "[]");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Handler1<Integer> nummerHandler = new Handler1<Integer>() {
|
||||
@Override
|
||||
public RouteResult handle(RequestContext ctx, Integer integer) {
|
||||
return ctx.completeAs(numberAsJsonListMarshaller, integer);
|
||||
}
|
||||
};
|
||||
|
||||
TestRoute route =
|
||||
testRoute(
|
||||
get(
|
||||
path("nummer").route(
|
||||
handleWith(n, nummerHandler)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
route.run(HttpRequest.GET("/nummer?n=1"))
|
||||
.assertStatusCode(200)
|
||||
.assertMediaType(MediaTypes.APPLICATION_JSON)
|
||||
.assertEntity("[1]");
|
||||
|
||||
route.run(HttpRequest.GET("/nummer?n=5"))
|
||||
.assertStatusCode(200)
|
||||
.assertEntity("[1,2,3,4,5]");
|
||||
|
||||
route.run(
|
||||
HttpRequest.GET("/nummer?n=5").addHeader(Accept.create(MediaTypes.TEXT_PLAIN.toRange())))
|
||||
.assertStatusCode(406);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomToResponseMarshaller() {
|
||||
final Marshaller<Integer> numberAsJsonListMarshaller =
|
||||
Marshallers.toResponse(MediaTypes.APPLICATION_JSON.toContentType(), new Function<Integer, HttpResponse>() {
|
||||
@Override
|
||||
public HttpResponse apply(Integer param) throws Exception {
|
||||
switch(param) {
|
||||
case 1: return HttpResponse.create().withEntity(MediaTypes.APPLICATION_JSON.toContentType(), "[1]");
|
||||
case 5: return HttpResponse.create().withEntity(MediaTypes.APPLICATION_JSON.toContentType(), "[1,2,3,4,5]");
|
||||
default: return HttpResponse.create().withStatus(404);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Handler1<Integer> nummerHandler = new Handler1<Integer>() {
|
||||
@Override
|
||||
public RouteResult handle(RequestContext ctx, Integer integer) {
|
||||
return ctx.completeAs(numberAsJsonListMarshaller, integer);
|
||||
}
|
||||
};
|
||||
|
||||
TestRoute route =
|
||||
testRoute(
|
||||
get(
|
||||
path("nummer").route(
|
||||
handleWith(n, nummerHandler)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
route.run(HttpRequest.GET("/nummer?n=1"))
|
||||
.assertStatusCode(200)
|
||||
.assertMediaType(MediaTypes.APPLICATION_JSON)
|
||||
.assertEntity("[1]");
|
||||
|
||||
route.run(HttpRequest.GET("/nummer?n=5"))
|
||||
.assertStatusCode(200)
|
||||
.assertEntity("[1,2,3,4,5]");
|
||||
|
||||
route.run(HttpRequest.GET("/nummer?n=6"))
|
||||
.assertStatusCode(404);
|
||||
|
||||
route.run(HttpRequest.GET("/nummer?n=5").addHeader(Accept.create(MediaTypes.TEXT_PLAIN.toRange())))
|
||||
.assertStatusCode(406);
|
||||
}
|
||||
}
|
||||
|
|
@ -216,7 +216,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
|
|||
|
||||
@Test
|
||||
public void testIntegerMatcher() {
|
||||
PathMatcher<Integer> age = PathMatchers.integerNumber();
|
||||
PathMatcher<Integer> age = PathMatchers.intValue();
|
||||
|
||||
TestRoute route =
|
||||
testRoute(
|
||||
|
|
@ -233,8 +233,8 @@ public class PathDirectivesTest extends JUnitRouteTest {
|
|||
public void testTwoVals() {
|
||||
// tests that `x` and `y` have different identities which is important for
|
||||
// retrieving the values
|
||||
PathMatcher<Integer> x = PathMatchers.integerNumber();
|
||||
PathMatcher<Integer> y = PathMatchers.integerNumber();
|
||||
PathMatcher<Integer> x = PathMatchers.intValue();
|
||||
PathMatcher<Integer> y = PathMatchers.intValue();
|
||||
|
||||
TestRoute route =
|
||||
testRoute(
|
||||
|
|
@ -254,7 +254,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
|
|||
|
||||
@Test
|
||||
public void testHexIntegerMatcher() {
|
||||
PathMatcher<Integer> color = PathMatchers.hexIntegerNumber();
|
||||
PathMatcher<Integer> color = PathMatchers.hexIntValue();
|
||||
|
||||
TestRoute route =
|
||||
testRoute(
|
||||
|
|
@ -267,7 +267,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
|
|||
|
||||
@Test
|
||||
public void testLongMatcher() {
|
||||
PathMatcher<Long> bigAge = PathMatchers.longNumber();
|
||||
PathMatcher<Long> bigAge = PathMatchers.longValue();
|
||||
|
||||
TestRoute route =
|
||||
testRoute(
|
||||
|
|
@ -280,7 +280,7 @@ public class PathDirectivesTest extends JUnitRouteTest {
|
|||
|
||||
@Test
|
||||
public void testHexLongMatcher() {
|
||||
PathMatcher<Long> code = PathMatchers.hexLongNumber();
|
||||
PathMatcher<Long> code = PathMatchers.hexLongValue();
|
||||
|
||||
TestRoute route =
|
||||
testRoute(
|
||||
|
|
|
|||
|
|
@ -6,16 +6,24 @@ package akka.http.impl.server
|
|||
|
||||
import akka.http.scaladsl.unmarshalling.{ Unmarshaller, FromStringUnmarshaller }
|
||||
import akka.http.scaladsl.util.FastFuture
|
||||
import akka.japi.function.Function
|
||||
import akka.japi.function
|
||||
|
||||
import scala.concurrent.{ Future, ExecutionContext }
|
||||
import scala.util.Try
|
||||
|
||||
object Util {
|
||||
def fromStringUnmarshallerFromFunction[T](convert: Function[String, T]): FromStringUnmarshaller[T] =
|
||||
def fromStringUnmarshallerFromFunction[T](convert: function.Function[String, T]): FromStringUnmarshaller[T] =
|
||||
scalaUnmarshallerFromFunction(convert)
|
||||
def scalaUnmarshallerFromFunction[T, U](convert: Function[T, U]): Unmarshaller[T, U] =
|
||||
|
||||
def scalaUnmarshallerFromFunction[T, U](convert: function.Function[T, U]): Unmarshaller[T, U] =
|
||||
new Unmarshaller[T, U] {
|
||||
def apply(value: T)(implicit ec: ExecutionContext): Future[U] = FastFuture(Try(convert(value)))
|
||||
}
|
||||
|
||||
implicit class JApiFunctionAndThen[T, U](f1: function.Function[T, U]) {
|
||||
def andThen[V](f2: U ⇒ V): function.Function[T, V] =
|
||||
new function.Function[T, V] {
|
||||
def apply(param: T): V = f2(f1(param))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,60 @@
|
|||
|
||||
package akka.http.javadsl.server
|
||||
|
||||
import akka.http.scaladsl.marshalling.ToResponseMarshaller
|
||||
import akka.http.javadsl.model._
|
||||
import akka.http.scaladsl.marshalling.{ ToResponseMarshaller, Marshaller ⇒ ScalaMarshaller }
|
||||
import akka.http.impl.server.MarshallerImpl
|
||||
import akka.http.scaladsl
|
||||
import akka.japi.function
|
||||
import akka.util.ByteString
|
||||
|
||||
/**
|
||||
* A collection of predefined marshallers.
|
||||
*/
|
||||
object Marshallers {
|
||||
def STRING: Marshaller[String] = MarshallerImpl(implicit ctx ⇒ implicitly[ToResponseMarshaller[String]])
|
||||
/**
|
||||
* A marshaller that marshals a String to a ``text/plain`` using a charset as negotiated with the
|
||||
* peer.
|
||||
*/
|
||||
def String: Marshaller[String] = MarshallerImpl(implicit ctx ⇒ implicitly[ToResponseMarshaller[String]])
|
||||
|
||||
import akka.http.impl.util.JavaMapping.Implicits._
|
||||
import akka.http.impl.server.Util._
|
||||
|
||||
/**
|
||||
* Creates a marshaller by specifying a media type and conversion function from ``T`` to String.
|
||||
* The charset for encoding the response will be negotiated with the client.
|
||||
*/
|
||||
def toEntityString[T](mediaType: MediaType, convert: function.Function[T, String]): Marshaller[T] =
|
||||
MarshallerImpl(_ ⇒ ScalaMarshaller.stringMarshaller(mediaType.asScala).compose[T](convert(_)))
|
||||
|
||||
/**
|
||||
* Creates a marshaller from a ContentType and a conversion function from ``T`` to a ``Array[Byte]``.
|
||||
*/
|
||||
def toEntityBytes[T](contentType: ContentType, convert: function.Function[T, Array[Byte]]): Marshaller[T] =
|
||||
toEntity(contentType, convert.andThen(scaladsl.model.HttpEntity(contentType.asScala, _)))
|
||||
|
||||
/**
|
||||
* Creates a marshaller from a ContentType and a conversion function from ``T`` to a ``ByteString``.
|
||||
*/
|
||||
def toEntityByteString[T](contentType: ContentType, convert: function.Function[T, ByteString]): Marshaller[T] =
|
||||
toEntity(contentType, convert.andThen(scaladsl.model.HttpEntity(contentType.asScala, _)))
|
||||
|
||||
/**
|
||||
* Creates a marshaller from a ContentType and a conversion function from ``T`` to a ``ResponseEntity``.
|
||||
*/
|
||||
def toEntity[T](contentType: ContentType, convert: function.Function[T, ResponseEntity]): Marshaller[T] =
|
||||
MarshallerImpl { _ ⇒
|
||||
ScalaMarshaller.withFixedCharset(contentType.mediaType().asScala, contentType.charset().asScala)(t ⇒
|
||||
HttpResponse.create().withStatus(200).withEntity(convert(t)).asScala)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a marshaller from a ContentType and a conversion function from ``T`` to an ``HttpResponse``.
|
||||
*/
|
||||
def toResponse[T](contentType: ContentType, convert: function.Function[T, HttpResponse]): Marshaller[T] =
|
||||
MarshallerImpl { _ ⇒
|
||||
ScalaMarshaller.withFixedCharset(contentType.mediaType().asScala, contentType.charset().asScala)(t ⇒
|
||||
convert(t).asScala)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.{ ContentType, StatusCode, HttpResponse }
|
||||
import akka.http.javadsl.model.{ Uri, ContentType, StatusCode, HttpResponse }
|
||||
import akka.http.javadsl.server._
|
||||
import akka.http.impl.server.RouteStructure._
|
||||
import akka.http.impl.server._
|
||||
|
|
@ -16,7 +16,7 @@ import scala.concurrent.Future
|
|||
|
||||
abstract class BasicDirectives {
|
||||
/**
|
||||
* Tries the given routes in sequence until the first one matches.
|
||||
* Tries the given route alternatives in sequence until the first one matches.
|
||||
*/
|
||||
@varargs
|
||||
def route(innerRoute: Route, moreInnerRoutes: Route*): Route =
|
||||
|
|
@ -63,6 +63,13 @@ abstract class BasicDirectives {
|
|||
def handle(ctx: RequestContext): RouteResult = ctx.completeAs(marshaller, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the request with redirection response of the given type to the given URI.
|
||||
*
|
||||
* The ``redirectionType`` must be a StatusCode for which ``isRedirection`` returns true.
|
||||
*/
|
||||
def redirect(uri: Uri, redirectionType: StatusCode): Route = Redirect(uri, redirectionType)
|
||||
|
||||
/**
|
||||
* A route that extracts a value and completes the request with it.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -15,46 +15,149 @@ import scala.collection.immutable
|
|||
|
||||
abstract class PathDirectives extends MiscDirectives {
|
||||
/**
|
||||
* Tries to consumes the complete unmatched path given a number of PathMatchers. Between each
|
||||
* of the matchers a `/` will be matched automatically.
|
||||
* Applies the given PathMatchers to the remaining unmatched path after consuming a leading slash.
|
||||
* The matcher has to match the remaining path completely.
|
||||
* If matched the value extracted by the PathMatchers is extracted on the directive level.
|
||||
*
|
||||
* A matcher can either be a matcher of type `PathMatcher`, or a literal string.
|
||||
* Each of Each of the arguments s must either be an instance of [[PathMatcher]] or a constant String
|
||||
* that will be automatically converted using `PathMatcher.segment`.
|
||||
*/
|
||||
@varargs
|
||||
def path(matchers: AnyRef*): Directive =
|
||||
RawPathPrefixForMatchers(joinWithSlash(convertMatchers(matchers)) :+ PathMatchers.END)
|
||||
|
||||
/**
|
||||
* Applies the given PathMatchers to a prefix of the remaining unmatched path after consuming a leading slash.
|
||||
* The matcher has to match a prefix of the remaining path.
|
||||
* If matched the value extracted by the PathMatcher is extracted on the directive level.
|
||||
*
|
||||
* Each of the arguments must either be an instance of [[PathMatcher]] or a constant String
|
||||
* that will be automatically converted using `PathMatcher.segment`.
|
||||
*/
|
||||
@varargs
|
||||
def pathPrefix(matchers: AnyRef*): Directive =
|
||||
RawPathPrefixForMatchers(joinWithSlash(convertMatchers(matchers)))
|
||||
|
||||
/**
|
||||
* Checks whether the unmatchedPath of the [[RequestContext]] has a prefix matched by the
|
||||
* given PathMatcher. In analogy to the `pathPrefix` directive a leading slash is implied.
|
||||
*
|
||||
* Each of the arguments must either be an instance of [[PathMatcher]] or a constant String
|
||||
* that will be automatically converted using `PathMatcher.segment`.
|
||||
*/
|
||||
@varargs
|
||||
def pathPrefixTest(matchers: AnyRef*): Directive =
|
||||
RawPathPrefixTestForMatchers(joinWithSlash(convertMatchers(matchers)))
|
||||
|
||||
/**
|
||||
* Applies the given matcher directly to a prefix of the unmatched path of the
|
||||
* [[RequestContext]] (i.e. without implicitly consuming a leading slash).
|
||||
* The matcher has to match a prefix of the remaining path.
|
||||
* If matched the value extracted by the PathMatcher is extracted on the directive level.
|
||||
*
|
||||
* Each of the arguments must either be an instance of [[PathMatcher]] or a constant String
|
||||
* that will be automatically converted using `PathMatcher.segment`.
|
||||
*/
|
||||
@varargs
|
||||
def rawPathPrefix(matchers: AnyRef*): Directive =
|
||||
RawPathPrefixForMatchers(convertMatchers(matchers))
|
||||
|
||||
/**
|
||||
* Checks whether the unmatchedPath of the [[RequestContext]] has a prefix matched by the
|
||||
* given PathMatcher. However, as opposed to the `pathPrefix` directive the matched path is not
|
||||
* actually "consumed".
|
||||
*
|
||||
* Each of the arguments must either be an instance of [[PathMatcher]] or a constant String
|
||||
* that will be automatically converted using `PathMatcher.segment`.
|
||||
*/
|
||||
@varargs
|
||||
def rawPathPrefixTest(matchers: AnyRef*): Directive =
|
||||
RawPathPrefixTestForMatchers(convertMatchers(matchers))
|
||||
|
||||
/**
|
||||
* Applies the given PathMatchers to a suffix of the remaining unmatchedPath of the [[RequestContext]].
|
||||
* If matched the value extracted by the PathMatchers is extracted and the matched parts of the path are consumed.
|
||||
* Note that, for efficiency reasons, the given PathMatchers must match the desired suffix in reversed-segment
|
||||
* order, i.e. `pathSuffix("baz" / "bar")` would match `/foo/bar/baz`!
|
||||
*
|
||||
* Each of the arguments must either be an instance of [[PathMatcher]] or a constant String
|
||||
* that will be automatically converted using `PathMatcher.segment`.
|
||||
*/
|
||||
@varargs
|
||||
def pathSuffix(matchers: AnyRef*): Directive =
|
||||
Directives.custom(RouteStructure.PathSuffix(convertMatchers(matchers)))
|
||||
|
||||
/**
|
||||
* Checks whether the unmatchedPath of the [[RequestContext]] has a suffix matched by the
|
||||
* given PathMatcher. However, as opposed to the pathSuffix directive the matched path is not
|
||||
* actually "consumed".
|
||||
* Note that, for efficiency reasons, the given PathMatcher must match the desired suffix in reversed-segment
|
||||
* order, i.e. `pathSuffixTest("baz" / "bar")` would match `/foo/bar/baz`!
|
||||
*
|
||||
* Each of the arguments must either be an instance of [[PathMatcher]] or a constant String
|
||||
* that will be automatically converted using `PathMatcher.segment`.
|
||||
*/
|
||||
@varargs
|
||||
def pathSuffixTest(matchers: AnyRef*): Directive =
|
||||
Directives.custom(RouteStructure.PathSuffixTest(convertMatchers(matchers)))
|
||||
|
||||
/**
|
||||
* Rejects the request if the unmatchedPath of the [[RequestContext]] is non-empty,
|
||||
* or said differently: only passes on the request to its inner route if the request path
|
||||
* has been matched completely.
|
||||
*/
|
||||
def pathEnd: Directive = RawPathPrefixForMatchers(PathMatchers.END :: Nil)
|
||||
def pathSingleSlash: Directive = RawPathPrefixForMatchers(List(PathMatchers.SLASH, PathMatchers.END))
|
||||
|
||||
/**
|
||||
* Only passes on the request to its inner route if the request path has been matched
|
||||
* completely or only consists of exactly one remaining slash.
|
||||
*
|
||||
* Note that trailing slash and non-trailing slash URLs are '''not''' the same, although they often serve
|
||||
* the same content. It is recommended to serve only one URL version and make the other redirect to it using
|
||||
* [[redirectToTrailingSlashIfMissing]] or [[redirectToNoTrailingSlashIfPresent]] directive.
|
||||
*
|
||||
* For example:
|
||||
* {{{
|
||||
* def route = {
|
||||
* // redirect '/users/' to '/users', '/users/:userId/' to '/users/:userId'
|
||||
* redirectToNoTrailingSlashIfPresent(Found) {
|
||||
* pathPrefix("users") {
|
||||
* pathEnd {
|
||||
* // user list ...
|
||||
* } ~
|
||||
* path(UUID) { userId =>
|
||||
* // user profile ...
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }}}
|
||||
*
|
||||
* For further information, refer to:
|
||||
* [[http://googlewebmastercentral.blogspot.de/2010/04/to-slash-or-not-to-slash.html]]
|
||||
*/
|
||||
def pathEndOrSingleSlash: Directive = RawPathPrefixForMatchers(List(PathMatchers.SLASH.optional, PathMatchers.END))
|
||||
|
||||
/**
|
||||
* Only passes on the request to its inner route if the request path
|
||||
* consists of exactly one remaining slash.
|
||||
*/
|
||||
def pathSingleSlash: Directive = RawPathPrefixForMatchers(List(PathMatchers.SLASH, PathMatchers.END))
|
||||
|
||||
/**
|
||||
* If the request path doesn't end with a slash, redirect to the same uri with trailing slash in the path.
|
||||
*
|
||||
* '''Caveat''': [[path]] without trailing slash and [[pathEnd]] directives will not match inside of this directive.
|
||||
*/
|
||||
@varargs
|
||||
def redirectToTrailingSlashIfMissing(redirectionStatusCode: StatusCode, innerRoute: Route, moreInnerRoutes: Route*): Route =
|
||||
RedirectToTrailingSlashIfMissing(redirectionStatusCode)(innerRoute, moreInnerRoutes.toList)
|
||||
|
||||
/**
|
||||
* If the request path ends with a slash, redirect to the same uri without trailing slash in the path.
|
||||
*
|
||||
* '''Caveat''': [[pathSingleSlash]] directive will not match inside of this directive.
|
||||
*/
|
||||
@varargs
|
||||
def redirectToNoTrailingSlashIfPresent(redirectionStatusCode: StatusCode, innerRoute: Route, moreInnerRoutes: Route*): Route =
|
||||
RedirectToNoTrailingSlashIfPresent(redirectionStatusCode)(innerRoute, moreInnerRoutes.toList)
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.javadsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.impl.server.RouteStructure.Redirect
|
||||
import akka.http.javadsl.model.{ StatusCode, Uri }
|
||||
|
||||
abstract class RouteDirectives extends RangeDirectives {
|
||||
/**
|
||||
* Completes the request with redirection response of the given type to the given URI.
|
||||
*
|
||||
* The ``redirectionType`` must be a StatusCode for which ``isRedirection`` returns true.
|
||||
*/
|
||||
def redirect(uri: Uri, redirectionType: StatusCode): Route = Redirect(uri, redirectionType)
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import akka.http.javadsl.server.Route
|
|||
|
||||
import scala.annotation.varargs
|
||||
|
||||
abstract class SchemeDirectives extends RouteDirectives {
|
||||
abstract class SchemeDirectives extends RangeDirectives {
|
||||
/**
|
||||
* Rejects all requests whose Uri scheme does not match the given one.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package akka.http.javadsl.server.values
|
||||
|
||||
import java.util.regex.Pattern
|
||||
import java.{ lang ⇒ jl, util ⇒ ju }
|
||||
|
||||
import akka.http.impl.server.PathMatcherImpl
|
||||
|
|
@ -14,6 +15,7 @@ import akka.japi.function.Function
|
|||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.reflect.ClassTag
|
||||
import scala.util.matching.Regex
|
||||
|
||||
/**
|
||||
* A PathMatcher is used to match the (yet unmatched) URI path of incoming requests.
|
||||
|
|
@ -31,24 +33,96 @@ trait PathMatcher[T] extends RequestVal[T] {
|
|||
* A collection of predefined path matchers.
|
||||
*/
|
||||
object PathMatchers {
|
||||
/**
|
||||
* A PathMatcher that always matches, doesn't consume anything and extracts nothing.
|
||||
* Serves mainly as a neutral element in PathMatcher composition.
|
||||
*/
|
||||
val NEUTRAL: PathMatcher[Void] = matcher0(_.Neutral)
|
||||
|
||||
/**
|
||||
* A PathMatcher that matches a single slash character ('/').
|
||||
*/
|
||||
val SLASH: PathMatcher[Void] = matcher0(_.Slash)
|
||||
|
||||
/**
|
||||
* A PathMatcher that matches the very end of the requests URI path.
|
||||
*/
|
||||
val END: PathMatcher[Void] = matcher0(_.PathEnd)
|
||||
|
||||
/**
|
||||
* Creates a PathMatcher that consumes (a prefix of) the first path segment
|
||||
* (if the path begins with a segment) and extracts a given value.
|
||||
*/
|
||||
def segment(name: String): PathMatcher[String] = matcher(_ ⇒ name -> name)
|
||||
|
||||
def integerNumber: PathMatcher[jl.Integer] = matcher(_.IntNumber.asInstanceOf[PathMatcher1[jl.Integer]])
|
||||
def hexIntegerNumber: PathMatcher[jl.Integer] = matcher(_.HexIntNumber.asInstanceOf[PathMatcher1[jl.Integer]])
|
||||
/**
|
||||
* A PathMatcher that efficiently matches a number of digits and extracts their (non-negative) Int value.
|
||||
* The matcher will not match 0 digits or a sequence of digits that would represent an Int value larger
|
||||
* than [[java.lang.Integer.MAX_VALUE]].
|
||||
*/
|
||||
def intValue: PathMatcher[jl.Integer] = matcher(_.IntNumber.asInstanceOf[PathMatcher1[jl.Integer]])
|
||||
|
||||
def longNumber: PathMatcher[jl.Long] = matcher(_.LongNumber.asInstanceOf[PathMatcher1[jl.Long]])
|
||||
def hexLongNumber: PathMatcher[jl.Long] = matcher(_.HexLongNumber.asInstanceOf[PathMatcher1[jl.Long]])
|
||||
/**
|
||||
* A PathMatcher that efficiently matches a number of hex-digits and extracts their (non-negative) Int value.
|
||||
* The matcher will not match 0 digits or a sequence of digits that would represent an Int value larger
|
||||
* than [[java.lang.Integer.MAX_VALUE]].
|
||||
*/
|
||||
def hexIntValue: PathMatcher[jl.Integer] = matcher(_.HexIntNumber.asInstanceOf[PathMatcher1[jl.Integer]])
|
||||
|
||||
/**
|
||||
* A PathMatcher that efficiently matches a number of digits and extracts their (non-negative) Long value.
|
||||
* The matcher will not match 0 digits or a sequence of digits that would represent an Long value larger
|
||||
* than [[java.lang.Long.MAX_VALUE]].
|
||||
*/
|
||||
def longValue: PathMatcher[jl.Long] = matcher(_.LongNumber.asInstanceOf[PathMatcher1[jl.Long]])
|
||||
|
||||
/**
|
||||
* A PathMatcher that efficiently matches a number of hex-digits and extracts their (non-negative) Long value.
|
||||
* The matcher will not match 0 digits or a sequence of digits that would represent an Long value larger
|
||||
* than [[java.lang.Long.MAX_VALUE]].
|
||||
*/
|
||||
def hexLongValue: PathMatcher[jl.Long] = matcher(_.HexLongNumber.asInstanceOf[PathMatcher1[jl.Long]])
|
||||
|
||||
/**
|
||||
* Creates a PathMatcher that consumes (a prefix of) the first path segment
|
||||
* if the path begins with a segment (a prefix of) which matches the given regex.
|
||||
* Extracts either the complete match (if the regex doesn't contain a capture group) or
|
||||
* the capture group (if the regex contains exactly one).
|
||||
* If the regex contains more than one capture group the method throws an IllegalArgumentException.
|
||||
*/
|
||||
def regex(regex: String): PathMatcher[String] = matcher(_ ⇒ regex.r)
|
||||
|
||||
/**
|
||||
* A PathMatcher that matches and extracts a java.util.UUID instance.
|
||||
*/
|
||||
def uuid: PathMatcher[ju.UUID] = matcher(_.JavaUUID)
|
||||
|
||||
/**
|
||||
* A PathMatcher that matches if the unmatched path starts with a path segment.
|
||||
* If so the path segment is extracted as a String.
|
||||
*/
|
||||
def segment: PathMatcher[String] = matcher(_.Segment)
|
||||
|
||||
/**
|
||||
* A PathMatcher that matches up to 128 remaining segments as a List[String].
|
||||
* This can also be no segments resulting in the empty list.
|
||||
* If the path has a trailing slash this slash will *not* be matched.
|
||||
*/
|
||||
def segments: PathMatcher[ju.List[String]] = matcher(_.Segments.map(_.asJava))
|
||||
|
||||
/**
|
||||
* A PathMatcher that matches the given number of path segments (separated by slashes) as a List[String].
|
||||
* If there are more than ``count`` segments present the remaining ones will be left unmatched.
|
||||
* If the path has a trailing slash this slash will *not* be matched.
|
||||
*/
|
||||
def segments(maxNumber: Int): PathMatcher[ju.List[String]] = matcher(_.Segments(maxNumber).map(_.asJava))
|
||||
|
||||
/**
|
||||
* A PathMatcher that matches and extracts the complete remaining,
|
||||
* unmatched part of the request's URI path as an (encoded!) String.
|
||||
* If you need access to the remaining unencoded elements of the path
|
||||
* use the `RestPath` matcher!
|
||||
*/
|
||||
def rest: PathMatcher[String] = matcher(_.Rest)
|
||||
|
||||
def segmentFromString[T](convert: Function[String, T], clazz: Class[T]): PathMatcher[T] =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue