Merge pull request #18663 from ktoso/wip-directives-java-ktoso
=doc,htp #18657 document Headers/FormField request values
This commit is contained in:
commit
9293c4b312
11 changed files with 287 additions and 34 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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): _*))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ⇒
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue