diff --git a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java b/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java index 5c5f5ebc99..983382b727 100644 --- a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java +++ b/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java @@ -23,6 +23,8 @@ public class SimpleServerApp extends HttpApp { static PathMatcher xSegment = PathMatchers.integerNumber(); static PathMatcher ySegment = PathMatchers.integerNumber(); + static RequestVal bodyAsName = RequestVals.entityAs(Unmarshallers.String()); + public static RouteResult multiply(RequestContext ctx, int x, int y) { int result = x * y; return ctx.complete(String.format("%d * %d = %d", x, y, result)); @@ -52,6 +54,13 @@ public class SimpleServerApp extends HttpApp { return ctx.complete(String.format("%d - %d = %d", xVal, yVal, result)); } }; + Handler1 helloPostHandler = + new Handler1() { + @Override + public RouteResult handle(RequestContext ctx, String s) { + return ctx.complete("Hello " + s + "!"); + } + }; return route( // matches the empty path @@ -73,6 +82,11 @@ public class SimpleServerApp extends HttpApp { path("multiplyAsync", xSegment, ySegment).route( // bind async handler by reflection handleWith(SimpleServerApp.class, "multiplyAsync", xSegment, ySegment) + ), + post( + path("hello").route( + handleWith(bodyAsName, helloPostHandler) + ) ) ); } diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/simple/SimpleServerTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/simple/SimpleServerTest.java index 5c65cbb947..b5b51c10dd 100644 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/simple/SimpleServerTest.java +++ b/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/simple/SimpleServerTest.java @@ -28,4 +28,13 @@ public class SimpleServerTest extends JUnitRouteTest { .assertStatusCode(200) .assertEntity("42 * 23 = 966"); } + + @Test + public void testPostWithBody() { + TestResponse response = route.run(HttpRequest.POST("/hello").withEntity("John")); + + response + .assertStatusCode(200) + .assertEntity("Hello John!"); + } } diff --git a/akka-http/src/main/scala/akka/http/impl/server/Util.scala b/akka-http/src/main/scala/akka/http/impl/server/Util.scala new file mode 100644 index 0000000000..9c8ab9615f --- /dev/null +++ b/akka-http/src/main/scala/akka/http/impl/server/Util.scala @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2009-2015 Typesafe Inc. + */ + +package akka.http.impl.server + +import akka.http.scaladsl.unmarshalling.{ Unmarshaller, FromStringUnmarshaller } +import akka.http.scaladsl.util.FastFuture +import akka.japi.function.Function + +import scala.concurrent.{ Future, ExecutionContext } +import scala.util.Try + +object Util { + def fromStringUnmarshallerFromFunction[T](convert: Function[String, T]): FromStringUnmarshaller[T] = + scalaUnmarshallerFromFunction(convert) + def scalaUnmarshallerFromFunction[T, U](convert: Function[T, U]): Unmarshaller[T, U] = + new Unmarshaller[T, U] { + def apply(value: T)(implicit ec: ExecutionContext): Future[U] = FastFuture(Try(convert(value))) + } +} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/CustomRequestVal.scala b/akka-http/src/main/scala/akka/http/javadsl/server/CustomRequestVal.scala new file mode 100644 index 0000000000..1ced44426f --- /dev/null +++ b/akka-http/src/main/scala/akka/http/javadsl/server/CustomRequestVal.scala @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2009-2015 Typesafe Inc. + */ + +package akka.http.javadsl.server + +import akka.http.impl.server.{ RequestContextImpl, StandaloneExtractionImpl } +import akka.http.scaladsl.server.Directive1 +import akka.http.scaladsl.server.directives.BasicDirectives._ + +import scala.reflect.ClassTag + +/** + * Extend from this class and implement ``extractValue`` to create a custom request val. + */ +abstract class CustomRequestVal[T](clazz: Class[T]) extends StandaloneExtractionImpl[T]()(ClassTag(clazz)) { + final def directive: Directive1[T] = extract(ctx ⇒ extractValue(RequestContextImpl(ctx))) + + protected def extractValue(ctx: RequestContext): T +} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala b/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala index eadb955780..54aadf1fdb 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala @@ -4,13 +4,33 @@ package akka.http.javadsl.server -import akka.http.impl.server.UnmarshallerImpl -import akka.http.scaladsl.unmarshalling.{ FromMessageUnmarshaller, PredefinedFromEntityUnmarshallers } +import akka.http.impl.server.{ Util, UnmarshallerImpl } +import akka.http.javadsl.model.{ HttpEntity, HttpMessage } +import akka.http.scaladsl.unmarshalling.{ Unmarshaller ⇒ ScalaUnmarshaller, FromMessageUnmarshaller } +import akka.japi.function.Function +import akka.stream.Materializer +import akka.util.ByteString + +import scala.reflect.ClassTag object Unmarshallers { - def String: Unmarshaller[String] = - new UnmarshallerImpl[String]({ (ec, mat) ⇒ - implicit val _ = mat - implicitly[FromMessageUnmarshaller[String]] + def String: Unmarshaller[String] = withMat(implicit mat ⇒ implicitly) + def ByteString: Unmarshaller[ByteString] = withMat(implicit mat ⇒ implicitly) + def ByteArray: Unmarshaller[Array[Byte]] = withMat(implicit mat ⇒ implicitly) + def CharArray: Unmarshaller[Array[Char]] = withMat(implicit mat ⇒ implicitly) + + def fromMessage[T](convert: Function[HttpMessage, T], clazz: Class[T]): Unmarshaller[T] = + new UnmarshallerImpl[T]({ (ec, mat) ⇒ + Util.scalaUnmarshallerFromFunction(convert) + })(ClassTag(clazz)) + + def fromEntity[T](convert: Function[HttpEntity, T], clazz: Class[T]): Unmarshaller[T] = + new UnmarshallerImpl[T]({ (ec, mat) ⇒ + ScalaUnmarshaller.messageUnmarshallerFromEntityUnmarshaller(Util.scalaUnmarshallerFromFunction[HttpEntity, T](convert)) + })(ClassTag(clazz)) + + private def withMat[T: ClassTag](f: Materializer ⇒ FromMessageUnmarshaller[T]): Unmarshaller[T] = + new UnmarshallerImpl[T]({ (ec, mat) ⇒ + f(mat) }) } diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/values/FormField.scala b/akka-http/src/main/scala/akka/http/javadsl/server/values/FormField.scala index 15ca4a9b2f..ffdbda4a6e 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/values/FormField.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/values/FormField.scala @@ -6,11 +6,14 @@ package akka.http.javadsl.server package values import java.{ lang ⇒ jl } +import akka.japi.function.Function import akka.japi.{ Option ⇒ JOption } -import akka.http.impl.server.FormFieldImpl +import akka.http.impl.server.{ Util, FormFieldImpl } import akka.http.scaladsl.unmarshalling._ +import scala.reflect.ClassTag + trait FormField[T] extends RequestVal[T] { def optional: RequestVal[JOption[T]] def withDefault(defaultValue: T): RequestVal[T] @@ -32,4 +35,9 @@ object FormFields { def hexShortValue(name: String): FormField[jl.Short] = FormFieldImpl(name.as(Unmarshaller.HexShort)) def hexIntValue(name: String): FormField[jl.Integer] = FormFieldImpl(name.as(Unmarshaller.HexInt)) def hexLongValue(name: String): FormField[jl.Long] = FormFieldImpl(name.as(Unmarshaller.HexLong)) + + def fromString[T](name: String, convert: Function[String, T], clazz: Class[T]): FormField[T] = { + implicit val tTag: ClassTag[T] = ClassTag(clazz) + FormFieldImpl(name.as(Util.fromStringUnmarshallerFromFunction(convert))) + } } \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/values/Parameter.scala b/akka-http/src/main/scala/akka/http/javadsl/server/values/Parameter.scala index ddd6fed5ed..b1c4865517 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/values/Parameter.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/values/Parameter.scala @@ -11,12 +11,13 @@ import java.util.{ Map ⇒ JMap, Collection ⇒ JCollection } import akka.http.scaladsl.server.directives.ParameterDirectives import akka.http.scaladsl.unmarshalling.Unmarshaller +import akka.japi.function.Function import scala.reflect.ClassTag import akka.japi.{ Option ⇒ JOption } -import akka.http.impl.server.{ StandaloneExtractionImpl, ParameterImpl } +import akka.http.impl.server.{ Util, StandaloneExtractionImpl, ParameterImpl } import akka.http.javadsl.server.RequestVal import akka.http.scaladsl.server.Directive1 import akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet @@ -63,5 +64,10 @@ object Parameters { StandaloneExtractionImpl(ParameterDirectives.parameterMultiMap.map(_.mapValues(_.asJavaCollection).asJava)) def asCollection: RequestVal[JCollection[JMap.Entry[String, String]]] = StandaloneExtractionImpl(ParameterDirectives.parameterSeq.map(_.map(e ⇒ new SimpleEntry(e._1, e._2): JMap.Entry[String, String]).asJavaCollection)) + + def fromString[T](name: String, convert: Function[String, T], clazz: Class[T]): Parameter[T] = { + implicit val tTag: ClassTag[T] = ClassTag(clazz) + ParameterImpl(name.as(Util.fromStringUnmarshallerFromFunction(convert))) + } } diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/values/PathMatchers.scala b/akka-http/src/main/scala/akka/http/javadsl/server/values/PathMatchers.scala index 9871328141..5bcf35e56e 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/values/PathMatchers.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/values/PathMatchers.scala @@ -8,8 +8,9 @@ import java.{ lang ⇒ jl, util ⇒ ju } import akka.http.impl.server.PathMatcherImpl import akka.http.javadsl.server.RequestVal -import akka.http.scaladsl.server.{ PathMatcher0, PathMatcher1, PathMatchers ⇒ ScalaPathMatchers } +import akka.http.scaladsl.server.{ PathMatcher0, PathMatcher1, PathMatchers ⇒ ScalaPathMatchers, PathMatcher ⇒ ScalaPathMatcher } import akka.japi.Option +import akka.japi.function.Function import scala.collection.JavaConverters._ import scala.reflect.ClassTag @@ -50,6 +51,9 @@ object PathMatchers { def rest: PathMatcher[String] = matcher(_.Rest) + def segmentFromString[T](convert: Function[String, T], clazz: Class[T]): PathMatcher[T] = + matcher(_ ⇒ ScalaPathMatchers.Segment.map(convert(_)))(ClassTag(clazz)) + private def matcher[T: ClassTag](scalaMatcher: ScalaPathMatchers.type ⇒ PathMatcher1[T]): PathMatcher[T] = new PathMatcherImpl[T](scalaMatcher(ScalaPathMatchers)) private def matcher0(scalaMatcher: ScalaPathMatchers.type ⇒ PathMatcher0): PathMatcher[Void] =