Merge pull request #18663 from ktoso/wip-directives-java-ktoso

=doc,htp #18657 document Headers/FormField request values
This commit is contained in:
Konrad Malawski 2015-10-09 15:19:29 +02:00
commit 9293c4b312
11 changed files with 287 additions and 34 deletions

View file

@ -0,0 +1,83 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
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;
import akka.http.javadsl.server.RequestVal;
import akka.http.javadsl.server.Route;
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 {
@Test
public void testFormFieldVals() {
//#simple
FormField<String> name = FormFields.stringValue("name");
FormField<Integer> age = FormFields.intValue("age");
final Route route =
route(
handleWith2(name, age, (ctx, n, a) ->
ctx.complete(String.format("Name: %s, age: %d", n, a))
)
);
// tests:
final FormData formData = FormData.create(
Pair.create("name", "Blippy"),
Pair.create("age", "42"));
final HttpRequest request =
HttpRequest
.POST("/")
.withEntity(formData.toEntity());
testRoute(route).run(request).assertEntity("Name: Blippy, age: 42");
//#simple
}
@Test
public void testFormFieldValsUnmarshaling() {
//#custom-unmarshal
FormField<SampleId> sampleId = FormFields.fromString("id", SampleId.class, s -> new SampleId(Integer.valueOf(s)));
final Route route =
route(
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("/")
.withEntity(formData.toEntity());
testRoute(route).run(request).assertEntity("SampleId: 1337");
//#custom-unmarshal
}
static class SampleId {
public final int id;
SampleId(int id) {
this.id = id;
}
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.javadsl.server;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.headers.Host;
import akka.http.javadsl.model.headers.RawHeader;
import akka.http.javadsl.server.RequestVal;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.server.values.Headers;
import akka.http.javadsl.testkit.JUnitRouteTest;
import org.junit.Test;
public class HeaderRequestValsExampleTest extends JUnitRouteTest {
@Test
public void testHeaderVals() {
//#by-class
// extract the entire header instance:
RequestVal<Host> host = Headers.byClass(Host.class).instance();
final Route route =
route(
handleWith1(host, (ctx, h) ->
ctx.complete(String.format("Host header was: %s", h.host()))
)
);
// tests:
final HttpRequest request =
HttpRequest
.GET("http://akka.io/")
.addHeader(Host.create("akka.io", 80));
testRoute(route).run(request).assertEntity("Host header was: akka.io");
//#by-class
}
@Test
public void testHeaderByName() {
//#by-name
// extract the `value` of the header:
final RequestVal<String> XFishName = Headers.byName("X-Fish-Name").value();
final Route route =
route(
handleWith1(XFishName, (ctx, xFishName) ->
ctx.complete(String.format("The `X-Fish-Name` header's value was: %s", xFishName))
)
);
// tests:
final HttpRequest request =
HttpRequest
.GET("/")
.addHeader(RawHeader.create("X-Fish-Name", "Blippy"));
testRoute(route).run(request).assertEntity("The `X-Fish-Name` header's value was: Blippy");
//#by-name
}
}

View file

@ -0,0 +1,25 @@
.. _form-field-request-vals-java:
Request Values: FormFields
==========================
A collection of pre-defined :ref:`request-vals-java` that can be used to extract header values from incoming requests.
Description
-----------
``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::
Examples
--------
Extracting form fields of a certain primitive type from a request:
.. includecode:: ../../../code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java#simple
Extracting values of custom type from a request by providing a conversion function:
.. includecode:: ../../../code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java#custom-unmarshal

View file

@ -0,0 +1,33 @@
.. _header-request-vals-java:
Request Values: Headers
=======================
A collection of pre-defined :ref:`request-vals-java` that can be used to extract header values from incoming requests.
Description
-----------
Header request values allow extracting ``HttpHeader`` 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<T> instance()
RequestVal<<Option<T>> optionalInstance()
RequestVal<String> value()
RequestVal<Option<String>> optionalValue()
Examples
--------
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
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

View file

@ -25,21 +25,21 @@ 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:
These request values are defined in the following objects:
RequestVals
:ref:`akka.http.javadsl.server.values.FormFields <form-field-request-vals-java>`
Contains request values for basic data like URI components, request method, peer address, or the entity data.
Cookies
akka.http.javadsl.server.values.FormFieldsCookies
Contains request values representing cookies.
FormFields
akka.http.javadsl.server.values.FormFields
Contains request values to access form fields unmarshalled to various primitive Java types.
Headers
:ref:`akka.http.javadsl.server.values.Headers <header-request-vals-java>`
Contains request values to access request headers or header values.
HttpBasicAuthenticator
akka.http.javadsl.server.values.FormFieldsHttpBasicAuthenticator
An abstract class to implement to create a request value representing a HTTP basic authenticated principal.
Parameters
akka.http.javadsl.server.values.FormFieldsParameters
Contains request values to access URI paramaters unmarshalled to various primitive Java types.
PathMatchers
akka.http.javadsl.server.values.FormFieldsPathMatchers
Contains request values to match and access URI path segments.
CustomRequestVal
akka.http.javadsl.server.values.FormFieldsCustomRequestVal
An abstract class to implement arbitrary custom request values.

View file

@ -0,0 +1,37 @@
/**
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
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<String, String>... fields) {
return akka.http.scaladsl.model.FormData.create(fields);
}
}

View file

@ -7,19 +7,19 @@ package akka.http.javadsl.model.headers;
import java.net.InetSocketAddress;
public abstract class Host extends akka.http.scaladsl.model.HttpHeader {
public static Host create(InetSocketAddress address) {
return akka.http.scaladsl.model.headers.Host.apply(address);
}
public static Host create(String host) {
return akka.http.scaladsl.model.headers.Host.apply(host);
}
public static Host create(String host, int port) {
return akka.http.scaladsl.model.headers.Host.apply(host, port);
}
public abstract akka.http.javadsl.model.Host host();
public abstract int port();
}

View file

@ -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: _*))
}
def create(fields: Array[akka.japi.Pair[String, String]]): FormData =
if (fields.isEmpty) Empty else FormData(Uri.Query(fields.map(_.toScala): _*))
}

View file

@ -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] {
@ -36,7 +36,8 @@ object FormFields {
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] = {
/** Unmarshals the `name` field using the provided `convert` function. */
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)))
}

View file

@ -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)))
}

View file

@ -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