diff --git a/akka-docs-dev/rst/java/code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java b/akka-docs-dev/rst/java/code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java index 8b44a5dc6d..d79636ba9a 100644 --- a/akka-docs-dev/rst/java/code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java +++ b/akka-docs-dev/rst/java/code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java @@ -5,6 +5,7 @@ package docs.http.javadsl.server; import akka.http.javadsl.model.ContentTypes; +import akka.http.javadsl.model.FormData; import akka.http.javadsl.model.HttpRequest; import akka.http.javadsl.model.headers.RawHeader; import akka.http.javadsl.server.Marshallers; @@ -14,7 +15,9 @@ import akka.http.javadsl.server.values.FormField; import akka.http.javadsl.server.values.FormFields; import akka.http.javadsl.server.values.Headers; import akka.http.javadsl.testkit.JUnitRouteTest; +import akka.japi.Pair; import docs.http.scaladsl.server.directives.Person; +import org.junit.Ignore; import org.junit.Test; public class FormFieldRequestValsExampleTest extends JUnitRouteTest { @@ -33,11 +36,14 @@ public class FormFieldRequestValsExampleTest extends JUnitRouteTest { ); // tests: + final FormData formData = FormData.create( + Pair.create("name", "Blippy"), + Pair.create("age", "42")); final HttpRequest request = HttpRequest - .POST("/"); - // .withFormData(); // FIXME awaits resolution of https://github.com/akka/akka/issues/18665 - testRoute(route).run(request).assertEntity("Name: ..., age: ..."); + .POST("/") + .withEntity(formData.toEntity()); + testRoute(route).run(request).assertEntity("Name: Blippy, age: 42"); //#simple } @@ -45,21 +51,22 @@ public class FormFieldRequestValsExampleTest extends JUnitRouteTest { @Test public void testFormFieldValsUnmarshaling() { //#custom-unmarshal - FormField sampleId = FormFields.fromString("id", s -> new SampleId(Integer.valueOf(s)), SampleId.class); + FormField sampleId = FormFields.fromString("id", SampleId.class, s -> new SampleId(Integer.valueOf(s))); final Route route = route( - handleWith1(sampleId, (ctx, id) -> - ctx.complete(String.format("SampleId: %s", id)) + handleWith1(sampleId, (ctx, sid) -> + ctx.complete(String.format("SampleId: %s", sid.id)) ) ); // tests: + final FormData formData = FormData.create(Pair.create("id", "1337")); final HttpRequest request = HttpRequest - .POST("/"); - // .withFormData(); // FIXME awaits resolution of https://github.com/akka/akka/issues/18665 - testRoute(route).run(request).assertEntity("Name: ..., age: ..."); + .POST("/") + .withEntity(formData.toEntity()); + testRoute(route).run(request).assertEntity("SampleId: 1337"); //#custom-unmarshal } @@ -72,4 +79,5 @@ public class FormFieldRequestValsExampleTest extends JUnitRouteTest { } } + } \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/routing-dsl/request-vals/form-field-request-vals.rst b/akka-docs-dev/rst/java/http/routing-dsl/request-vals/form-field-request-vals.rst index dfe95a1bfc..c72031a706 100644 --- a/akka-docs-dev/rst/java/http/routing-dsl/request-vals/form-field-request-vals.rst +++ b/akka-docs-dev/rst/java/http/routing-dsl/request-vals/form-field-request-vals.rst @@ -7,27 +7,19 @@ A collection of pre-defined :ref:`request-vals-java` that can be used to extract Description ----------- -Header request values allow extracting ``HttpHeader`` values or concrete instances from HTTP requests. +``FormField`` request values allow extracting fields submitted as ``application/x-www-form-urlencoded`` values or concrete instances from HTTP requests. The ``RequestVal`` builder is made up of 2 steps, initially you need to pick which Header to extract (``byName`` or ``byClass``) and then you need to pick if the header is optionally available or required (i.e. the route should not match if the header is not present in the request). This is done using one of the below depicted methods:: - RequestVal instance() - RequestVal<> optionalInstance() - - RequestVal value() - RequestVal> optionalValue() - Examples -------- -``Headers.byClass(Class[HttpHeader])`` +Extracting form fields of a certain primitive type from a request: -.. includecode:: ../../../code/docs/http/javadsl/server/HeaderRequestValsExampleTest.java - :include: by-class +.. includecode:: ../../../code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java#simple -``Headers.byName(String)`` +Extracting values of custom type from a request by providing a conversion function: -.. includecode:: ../../../code/docs/http/javadsl/server/HeaderRequestValsExampleTest.java - :include: by-name +.. includecode:: ../../../code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java#custom-unmarshal diff --git a/akka-docs-dev/rst/java/http/routing-dsl/request-vals/header-request-vals.rst b/akka-docs-dev/rst/java/http/routing-dsl/request-vals/header-request-vals.rst index 3f363d82c4..f73bf5317e 100644 --- a/akka-docs-dev/rst/java/http/routing-dsl/request-vals/header-request-vals.rst +++ b/akka-docs-dev/rst/java/http/routing-dsl/request-vals/header-request-vals.rst @@ -22,12 +22,12 @@ match if the header is not present in the request). This is done using one of th Examples -------- -``Headers.byClass(Class[HttpHeader])`` +Extracting a header by using a specific ``Header`` class (which are pre-defined in ``akka.http.javadsl.model.headers.*``): .. includecode:: ../../../code/docs/http/javadsl/server/HeaderRequestValsExampleTest.java :include: by-class -``Headers.byName(String)`` +Extracting arbitrary headers by their name, for example custom headers (usually starting with ``X-...``): .. includecode:: ../../../code/docs/http/javadsl/server/HeaderRequestValsExampleTest.java :include: by-name diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/FormData.java b/akka-http-core/src/main/java/akka/http/javadsl/model/FormData.java new file mode 100644 index 0000000000..1936758d72 --- /dev/null +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/FormData.java @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package akka.http.javadsl.model; + +import akka.http.impl.model.parser.CharacterClasses; +import akka.http.impl.util.JavaMapping; +import akka.http.impl.util.StringRendering; +import akka.http.scaladsl.model.Uri.Query; +import akka.http.scaladsl.model.UriRendering; +import akka.japi.Pair; + +/** + * Simple model for `application/x-www-form-urlencoded` form data. + */ +public abstract class FormData { + + public abstract Query fields(); + + public RequestEntity toEntity() { + return toEntity(HttpCharsets.UTF_8); + } + + public RequestEntity toEntity(HttpCharset charset) { + // TODO this logic is duplicated in scaladsl.model.FormData, spent hours trying to DRY it but compiler freaked out in a number of ways... -- ktoso + final akka.http.scaladsl.model.HttpCharset c = (akka.http.scaladsl.model.HttpCharset) charset; + final StringRendering render = (StringRendering) UriRendering.renderQuery(new StringRendering(), this.fields(), c.nioCharset(), CharacterClasses.unreserved()); + return HttpEntities.create(ContentType.create(MediaTypes.APPLICATION_X_WWW_FORM_URLENCODED, charset), render.get()); + } + + @SafeVarargs + public static FormData create(Pair... fields) { + return akka.http.scaladsl.model.FormData.create(fields); + } + +} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/FormData.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/FormData.scala index 5f208471b3..6ad16b3d9c 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/FormData.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/model/FormData.scala @@ -4,10 +4,25 @@ package akka.http.scaladsl.model +import akka.http.impl.model.parser.CharacterClasses +import akka.http.impl.util.StringRendering +import akka.http.javadsl.{ model ⇒ jm } +import akka.http.scaladsl.model.HttpCharsets._ +import akka.http.scaladsl.model.MediaTypes._ + /** * Simple model for `application/x-www-form-urlencoded` form data. */ -final case class FormData(fields: Uri.Query) +final case class FormData(fields: Uri.Query) extends jm.FormData { + override def toEntity: akka.http.scaladsl.model.RequestEntity = + toEntity(HttpCharsets.`UTF-8`) + + def toEntity(charset: HttpCharset): akka.http.scaladsl.model.RequestEntity = { + // TODO this logic is duplicated in javadsl.model.FormData, spent hours trying to DRY it but compiler freaked out in a number of ways... -- ktoso + val render: StringRendering = UriRendering.renderQuery(new StringRendering, this.fields, charset.nioCharset, CharacterClasses.unreserved) + HttpEntity(ContentType(`application/x-www-form-urlencoded`, `UTF-8`), render.get) + } +} object FormData { val Empty = FormData(Uri.Query.Empty) @@ -17,4 +32,7 @@ object FormData { def apply(fields: (String, String)*): FormData = if (fields.isEmpty) Empty else FormData(Uri.Query(fields: _*)) -} \ No newline at end of file + + def create(fields: Array[akka.japi.Pair[String, String]]): FormData = + if (fields.isEmpty) Empty else FormData(Uri.Query(fields.map(_.toScala): _*)) +} 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 c2318537d3..beb9448dda 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,12 +6,12 @@ package akka.http.javadsl.server package values import java.{ lang ⇒ jl } + +import akka.http.impl.server.{ FormFieldImpl, Util } +import akka.http.scaladsl.unmarshalling._ import akka.japi.function.Function import akka.japi.{ Option ⇒ JOption } -import akka.http.impl.server.{ Util, FormFieldImpl } -import akka.http.scaladsl.unmarshalling._ - import scala.reflect.ClassTag trait FormField[T] extends RequestVal[T] { @@ -37,7 +37,7 @@ object FormFields { def hexLongValue(name: String): FormField[jl.Long] = FormFieldImpl(name.as(Unmarshaller.HexLong)) /** Unmarshals the `name` field using the provided `convert` function. */ - def fromString[T](name: String, convert: Function[String, T], clazz: Class[T]): FormField[T] = { + def fromString[T](name: String, clazz: Class[T], convert: Function[String, T]): FormField[T] = { implicit val tTag: ClassTag[T] = ClassTag(clazz) FormFieldImpl(name.as(Util.fromStringUnmarshallerFromFunction(convert))) } 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 b1c4865517..093d6e8967 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 @@ -5,22 +5,17 @@ package akka.http.javadsl.server.values import java.util.AbstractMap.SimpleEntry +import java.util.{ Collection ⇒ JCollection, Map ⇒ JMap } import java.{ lang ⇒ jl } -import java.util.{ Map ⇒ JMap, Collection ⇒ JCollection } - +import akka.http.impl.server.{ ParameterImpl, StandaloneExtractionImpl, Util } +import akka.http.javadsl.server.RequestVal 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.{ Util, StandaloneExtractionImpl, ParameterImpl } -import akka.http.javadsl.server.RequestVal -import akka.http.scaladsl.server.Directive1 -import akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet +import scala.reflect.ClassTag /** * A RequestVal representing a query parameter of type T. @@ -65,7 +60,8 @@ object Parameters { 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] = { + /** Unmarshals the `name` field using the provided `convert` function. */ + def fromString[T](name: String, clazz: Class[T], convert: Function[String, 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/scaladsl/marshalling/PredefinedToEntityMarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToEntityMarshallers.scala index 42ca0cf716..04e1f7aafe 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToEntityMarshallers.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToEntityMarshallers.scala @@ -5,10 +5,9 @@ package akka.http.scaladsl.marshalling import java.nio.CharBuffer -import akka.http.impl.model.parser.CharacterClasses + import akka.http.scaladsl.model.MediaTypes._ import akka.http.scaladsl.model._ -import akka.http.impl.util.StringRendering import akka.util.ByteString trait PredefinedToEntityMarshallers extends MultipartMarshallers { @@ -53,9 +52,7 @@ trait PredefinedToEntityMarshallers extends MultipartMarshallers { implicit val FormDataMarshaller: ToEntityMarshaller[FormData] = Marshaller.withOpenCharset(`application/x-www-form-urlencoded`) { (formData, charset) ⇒ - val query = Uri.Query(formData.fields: _*) - val string = UriRendering.renderQuery(new StringRendering, query, charset.nioCharset, CharacterClasses.unreserved).get - HttpEntity(ContentType(`application/x-www-form-urlencoded`, charset), string) + formData.toEntity(charset) } implicit val MessageEntityMarshaller: ToEntityMarshaller[MessageEntity] = Marshaller strict { value ⇒