You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2022/07/09 14:07:24 UTC

[juneau] 01/01: Add @Query(def) annotation.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch jbFixRestNpe
in repository https://gitbox.apache.org/repos/asf/juneau.git

commit 848c9e20563e75908623c9e01e4d570873943e5c
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Sat Jul 9 10:07:04 2022 -0400

    Add @Query(def) annotation.
---
 .../apache/juneau/http/annotation/FormData.java    |   7 ++
 .../juneau/http/annotation/FormDataAnnotation.java |  33 ++++++-
 .../org/apache/juneau/http/annotation/Header.java  |   7 ++
 .../juneau/http/annotation/HeaderAnnotation.java   |  33 ++++++-
 .../org/apache/juneau/http/annotation/Path.java    |   7 ++
 .../juneau/http/annotation/PathAnnotation.java     |  33 ++++++-
 .../org/apache/juneau/http/annotation/Query.java   |   7 ++
 .../juneau/http/annotation/QueryAnnotation.java    |  38 +++++--
 .../org/apache/juneau/rest/arg/FormDataArg.java    |   5 +-
 .../java/org/apache/juneau/rest/arg/HeaderArg.java |   5 +-
 .../java/org/apache/juneau/rest/arg/PathArg.java   |   5 +-
 .../java/org/apache/juneau/rest/arg/QueryArg.java  |   8 +-
 .../juneau/rest/httppart/RequestFormParam.java     |  13 +++
 .../apache/juneau/rest/httppart/RequestHeader.java |  14 ++-
 .../juneau/rest/httppart/RequestPathParam.java     |  14 ++-
 .../juneau/rest/httppart/RequestQueryParam.java    |  14 ++-
 .../http/annotation/FormDataAnnotation_Test.java   |   5 +
 .../http/annotation/HeaderAnnotation_Test.java     |   5 +
 .../http/annotation/PathAnnotation_Test.java       |   5 +
 .../http/annotation/QueryAnnotation_Test.java      |   5 +
 .../juneau/rest/annotation/FormData_Test.java      | 108 ++++++++++++++++++++
 .../apache/juneau/rest/annotation/Header_Test.java | 110 +++++++++++++++++++++
 .../apache/juneau/rest/annotation/Query_Test.java  | 103 +++++++++++++++++++
 23 files changed, 560 insertions(+), 24 deletions(-)

diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java
index 38171ac22..ddc248e9c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java
@@ -122,6 +122,13 @@ import org.apache.juneau.oapi.*;
 @ContextApply(FormDataAnnotation.Applier.class)
 public @interface FormData {
 
+	/**
+	 * Default value for this parameter.
+	 *
+	 * @return The annotation value.
+	 */
+	String def() default "";
+
 	/**
 	 * FORM parameter name.
 	 *
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormDataAnnotation.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormDataAnnotation.java
index 171f5ecc4..90aa0ff37 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormDataAnnotation.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormDataAnnotation.java
@@ -96,6 +96,18 @@ public class FormDataAnnotation {
 		return n;
 	}
 
+	/**
+	 * Finds the default value from the specified list of annotations.
+	 *
+	 * @param pi The parameter.
+	 * @return The last matching default value, or {@link Value#empty()} if not found.
+	 */
+	public static Value<String> findDef(ParamInfo pi) {
+		Value<String> n = Value.empty();
+		pi.forEachAnnotation(FormData.class, x -> isNotEmpty(x.def()), x -> n.set(x.def()));
+		return n;
+	}
+
 	//-----------------------------------------------------------------------------------------------------------------
 	// Builder
 	//-----------------------------------------------------------------------------------------------------------------
@@ -112,7 +124,7 @@ public class FormDataAnnotation {
 		Class<? extends HttpPartParser> parser = HttpPartParser.Void.class;
 		Class<? extends HttpPartSerializer> serializer = HttpPartSerializer.Void.class;
 		Schema schema = SchemaAnnotation.DEFAULT;
-		String name="", value="";
+		String def="", name="", value="";
 
 		/**
 		 * Constructor.
@@ -130,6 +142,17 @@ public class FormDataAnnotation {
 			return new Impl(this);
 		}
 
+		/**
+		 * Sets the {@link FormData#def} property on this annotation.
+		 *
+		 * @param value The new value for this property.
+		 * @return This object.
+		 */
+		public Builder def(String value) {
+			this.def = value;
+			return this;
+		}
+
 		/**
 		 * Sets the {@link FormData#name} property on this annotation.
 		 *
@@ -228,11 +251,12 @@ public class FormDataAnnotation {
 
 		private final Class<? extends HttpPartParser> parser;
 		private final Class<? extends HttpPartSerializer> serializer;
-		private final String name, value;
+		private final String name, value, def;
 		private final Schema schema;
 
 		Impl(Builder b) {
 			super(b);
+			this.def = b.def;
 			this.name = b.name;
 			this.parser = b.parser;
 			this.schema = b.schema;
@@ -241,6 +265,11 @@ public class FormDataAnnotation {
 			postConstruct();
 		}
 
+		@Override /* FormData */
+		public String def() {
+			return def;
+		}
+
 		@Override /* FormData */
 		public String name() {
 			return name;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java
index 5292cc8bb..0057df7d7 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java
@@ -88,6 +88,13 @@ import org.apache.juneau.oapi.*;
 @ContextApply(HeaderAnnotation.Applier.class)
 public @interface Header {
 
+	/**
+	 * Default value for this parameter.
+	 *
+	 * @return The annotation value.
+	 */
+	String def() default "";
+
 	/**
 	 * HTTP header name.
 	 * <p>
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/HeaderAnnotation.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/HeaderAnnotation.java
index 8d4154542..1358a0d30 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/HeaderAnnotation.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/HeaderAnnotation.java
@@ -97,6 +97,18 @@ public class HeaderAnnotation {
 		return n;
 	}
 
+	/**
+	 * Finds the default value from the specified list of annotations.
+	 *
+	 * @param pi The parameter.
+	 * @return The last matching default value, or {@link Value#empty()} if not found.
+	 */
+	public static Value<String> findDef(ParamInfo pi) {
+		Value<String> n = Value.empty();
+		pi.forEachAnnotation(Header.class, x -> isNotEmpty(x.def()), x -> n.set(x.def()));
+		return n;
+	}
+
 	//-----------------------------------------------------------------------------------------------------------------
 	// Builder
 	//-----------------------------------------------------------------------------------------------------------------
@@ -113,7 +125,7 @@ public class HeaderAnnotation {
 		Class<? extends HttpPartParser> parser = HttpPartParser.Void.class;
 		Class<? extends HttpPartSerializer> serializer = HttpPartSerializer.Void.class;
 		Schema schema = SchemaAnnotation.DEFAULT;
-		String name="", value="";
+		String name="", value="", def="";
 
 		/**
 		 * Constructor.
@@ -131,6 +143,17 @@ public class HeaderAnnotation {
 			return new Impl(this);
 		}
 
+		/**
+		 * Sets the {@link Header#def} property on this annotation.
+		 *
+		 * @param value The new value for this property.
+		 * @return This object.
+		 */
+		public Builder def(String value) {
+			this.def = value;
+			return this;
+		}
+
 		/**
 		 * Sets the {@link Header#name} property on this annotation.
 		 *
@@ -229,11 +252,12 @@ public class HeaderAnnotation {
 
 		private final Class<? extends HttpPartParser> parser;
 		private final Class<? extends HttpPartSerializer> serializer;
-		private final String name, value;
+		private final String name, value, def;
 		private final Schema schema;
 
 		Impl(Builder b) {
 			super(b);
+			this.def = b.def;
 			this.name = b.name;
 			this.parser = b.parser;
 			this.schema = b.schema;
@@ -242,6 +266,11 @@ public class HeaderAnnotation {
 			postConstruct();
 		}
 
+		@Override /* Header */
+		public String def() {
+			return def;
+		}
+
 		@Override /* Header */
 		public String name() {
 			return name;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Path.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Path.java
index d78dd307f..e87eed997 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Path.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Path.java
@@ -85,6 +85,13 @@ import org.apache.juneau.oapi.*;
 @ContextApply(PathAnnotation.Applier.class)
 public @interface Path {
 
+	/**
+	 * Default value for this parameter.
+	 *
+	 * @return The annotation value.
+	 */
+	String def() default "";
+
 	/**
 	 * URL path variable name.
 	 *
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/PathAnnotation.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/PathAnnotation.java
index 32c92609d..44d06f760 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/PathAnnotation.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/PathAnnotation.java
@@ -96,6 +96,18 @@ public class PathAnnotation {
 		return n;
 	}
 
+	/**
+	 * Finds the default value from the specified list of annotations.
+	 *
+	 * @param pi The parameter.
+	 * @return The last matching default value, or {@link Value#empty()} if not found.
+	 */
+	public static Value<String> findDef(ParamInfo pi) {
+		Value<String> n = Value.empty();
+		pi.forEachAnnotation(Path.class, x -> isNotEmpty(x.def()), x -> n.set(x.def()));
+		return n;
+	}
+
 	//-----------------------------------------------------------------------------------------------------------------
 	// Builder
 	//-----------------------------------------------------------------------------------------------------------------
@@ -112,7 +124,7 @@ public class PathAnnotation {
 		Class<? extends HttpPartParser> parser = HttpPartParser.Void.class;
 		Class<? extends HttpPartSerializer> serializer = HttpPartSerializer.Void.class;
 		Schema schema = SchemaAnnotation.DEFAULT;
-		String name="", value="";
+		String name="", value="", def="";
 
 		/**
 		 * Constructor.
@@ -141,6 +153,17 @@ public class PathAnnotation {
 			return this;
 		}
 
+		/**
+		 * Sets the {@link Path#name} property on this annotation.
+		 *
+		 * @param value The new value for this property.
+		 * @return This object.
+		 */
+		public Builder def(String value) {
+			this.def = value;
+			return this;
+		}
+
 		/**
 		 * Sets the {@link Path#parser} property on this annotation.
 		 *
@@ -228,11 +251,12 @@ public class PathAnnotation {
 
 		private final Class<? extends HttpPartParser> parser;
 		private final Class<? extends HttpPartSerializer> serializer;
-		private final String  name, value;
+		private final String  name, value, def;
 		private final Schema schema;
 
 		Impl(Builder b) {
 			super(b);
+			this.def = b.def;
 			this.name = b.name;
 			this.parser = b.parser;
 			this.schema = b.schema;
@@ -246,6 +270,11 @@ public class PathAnnotation {
 			return name;
 		}
 
+		@Override /* Path */
+		public String def() {
+			return def;
+		}
+
 		@Override /* Path */
 		public Class<? extends HttpPartParser> parser() {
 			return parser;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
index 01bf7fa41..05f1f4dd2 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
@@ -96,6 +96,13 @@ import org.apache.juneau.oapi.*;
 @ContextApply(QueryAnnotation.Applier.class)
 public @interface Query {
 
+	/**
+	 * Default value for this parameter.
+	 *
+	 * @return The annotation value.
+	 */
+	String def() default "";
+
 	/**
 	 * URL query parameter name.
 	 *
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/QueryAnnotation.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/QueryAnnotation.java
index f9fa1d108..3959b1972 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/QueryAnnotation.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/QueryAnnotation.java
@@ -81,10 +81,7 @@ public class QueryAnnotation {
 	}
 
 	/**
-	 * Finds the name from the specified lists of annotations.
-	 *
-	 * <p>
-	 * The last matching name found is returned.
+	 * Finds the name from the specified list of annotations.
 	 *
 	 * @param pi The parameter.
 	 * @return The last matching name, or {@link Value#empty()} if not found.
@@ -96,6 +93,18 @@ public class QueryAnnotation {
 		return n;
 	}
 
+	/**
+	 * Finds the default value from the specified list of annotations.
+	 *
+	 * @param pi The parameter.
+	 * @return The last matching default value, or {@link Value#empty()} if not found.
+	 */
+	public static Value<String> findDef(ParamInfo pi) {
+		Value<String> n = Value.empty();
+		pi.forEachAnnotation(Query.class, x -> isNotEmpty(x.def()), x -> n.set(x.def()));
+		return n;
+	}
+
 	//-----------------------------------------------------------------------------------------------------------------
 	// Builder
 	//-----------------------------------------------------------------------------------------------------------------
@@ -112,7 +121,7 @@ public class QueryAnnotation {
 		Class<? extends HttpPartParser> parser = HttpPartParser.Void.class;
 		Class<? extends HttpPartSerializer> serializer = HttpPartSerializer.Void.class;
 		Schema schema = SchemaAnnotation.DEFAULT;
-		String name="", value="";
+		String name="", value="", def="";
 
 		/**
 		 * Constructor.
@@ -130,6 +139,17 @@ public class QueryAnnotation {
 			return new Impl(this);
 		}
 
+		/**
+		 * Sets the {@link Query#def} property on this annotation.
+		 *
+		 * @param value The new value for this property.
+		 * @return This object.
+		 */
+		public Builder def(String value) {
+			this.def = value;
+			return this;
+		}
+
 		/**
 		 * Sets the {@link Query#name} property on this annotation.
 		 *
@@ -228,7 +248,7 @@ public class QueryAnnotation {
 
 		private final Class<? extends HttpPartParser> parser;
 		private final Class<? extends HttpPartSerializer> serializer;
-		private final String name, value;
+		private final String name, value, def;
 		private final Schema schema;
 
 		Impl(Builder b) {
@@ -238,6 +258,7 @@ public class QueryAnnotation {
 			this.schema = b.schema;
 			this.serializer = b.serializer;
 			this.value = b.value;
+			this.def = b.def;
 			postConstruct();
 		}
 
@@ -265,6 +286,11 @@ public class QueryAnnotation {
 		public String value() {
 			return value;
 		}
+
+		@Override /* Query */
+		public String def() {
+			return def;
+		}
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/FormDataArg.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/FormDataArg.java
index 20b8f3bd8..29c1655c1 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/FormDataArg.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/FormDataArg.java
@@ -57,7 +57,7 @@ public class FormDataArg implements RestOpArg {
 	private final boolean multi;
 	private final HttpPartParser partParser;
 	private final HttpPartSchema schema;
-	private final String name;
+	private final String name, def;
 	private final ClassInfo type;
 
 	/**
@@ -81,6 +81,7 @@ public class FormDataArg implements RestOpArg {
 	 */
 	protected FormDataArg(ParamInfo pi, AnnotationWorkList annotations) {
 		this.name = findName(pi).orElseThrow(()->new ArgException(pi, "@FormData used without name or value"));
+		this.def = findDef(pi).orElse(null);
 		this.type = pi.getParameterType();
 		this.schema = HttpPartSchema.create(FormData.class, pi);
 		Class<? extends HttpPartParser> pp = schema.getParser();
@@ -113,6 +114,6 @@ public class FormDataArg implements RestOpArg {
 			return req.getBeanSession().convertToType(m, cm);
 		}
 
-		return rh.getLast(name).parser(ps).schema(schema).as(type.innerType()).orElse(null);
+		return rh.getLast(name).parser(ps).schema(schema).def(def).as(type.innerType()).orElse(null);
 	}
 }
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/HeaderArg.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/HeaderArg.java
index e7d0ad0d1..4a1f211ad 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/HeaderArg.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/HeaderArg.java
@@ -98,7 +98,7 @@ public class HeaderArg implements RestOpArg {
 	private final HttpPartParser partParser;
 	private final HttpPartSchema schema;
 	private final boolean multi;
-	private final String name;
+	private final String name, def;
 	private final ClassInfo type;
 
 	/**
@@ -122,6 +122,7 @@ public class HeaderArg implements RestOpArg {
 	 */
 	protected HeaderArg(ParamInfo pi, AnnotationWorkList annotations) {
 		this.name = findName(pi).orElseThrow(() -> new ArgException(pi, "@Header used without name or value"));
+		this.def = findDef(pi).orElse(null);
 		this.type = pi.getParameterType();
 		this.schema = HttpPartSchema.create(Header.class, pi);
 		Class<? extends HttpPartParser> pp = schema.getParser();
@@ -154,6 +155,6 @@ public class HeaderArg implements RestOpArg {
 			return req.getBeanSession().convertToType(m, cm);
 		}
 
-		return rh.getLast(name).parser(ps).schema(schema).as(type.innerType()).orElse(null);
+		return rh.getLast(name).parser(ps).schema(schema).def(def).as(type.innerType()).orElse(null);
 	}
 }
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/PathArg.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/PathArg.java
index 3433e9ebd..69b80d384 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/PathArg.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/PathArg.java
@@ -50,7 +50,7 @@ import org.apache.juneau.rest.util.*;
 public class PathArg implements RestOpArg {
 	private final HttpPartParser partParser;
 	private final HttpPartSchema schema;
-	private final String name;
+	private final String name, def;
 	private final Type type;
 
 	/**
@@ -76,6 +76,7 @@ public class PathArg implements RestOpArg {
 	 */
 	protected PathArg(ParamInfo paramInfo, AnnotationWorkList annotations, UrlPathMatcher pathMatcher) {
 		this.name = getName(paramInfo, pathMatcher);
+		this.def = findDef(paramInfo).orElse(null);
 		this.type = paramInfo.getParameterType().innerType();
 		this.schema = HttpPartSchema.create(Path.class, paramInfo);
 		Class<? extends HttpPartParser> pp = schema.getParser();
@@ -119,6 +120,6 @@ public class PathArg implements RestOpArg {
 			return req.getBeanSession().convertToType(m, type);
 		}
 		HttpPartParserSession ps = partParser == null ? req.getPartParserSession() : partParser.getPartSession();
-		return req.getPathParams().get(name).parser(ps).schema(schema).as(type).orElse(null);
+		return req.getPathParams().get(name).parser(ps).schema(schema).def(def).as(type).orElse(null);
 	}
 }
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/QueryArg.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/QueryArg.java
index a8938ed2c..2e143726f 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/QueryArg.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/QueryArg.java
@@ -14,6 +14,7 @@ package org.apache.juneau.rest.arg;
 
 import static org.apache.juneau.internal.CollectionUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.http.annotation.QueryAnnotation.*;
 
 import java.util.*;
 
@@ -56,7 +57,7 @@ public class QueryArg implements RestOpArg {
 	private final boolean multi;
 	private final HttpPartParser partParser;
 	private final HttpPartSchema schema;
-	private final String name;
+	private final String name, def;
 	private final ClassInfo type;
 
 	/**
@@ -79,7 +80,8 @@ public class QueryArg implements RestOpArg {
 	 * @param annotations The annotations to apply to any new part parsers.
 	 */
 	protected QueryArg(ParamInfo pi, AnnotationWorkList annotations) {
-		this.name = QueryAnnotation.findName(pi).orElseThrow(() -> new ArgException(pi, "@Query used without name or value"));
+		this.name = findName(pi).orElseThrow(() -> new ArgException(pi, "@Query used without name or value"));
+		this.def = findDef(pi).orElse(null);
 		this.type = pi.getParameterType();
 		this.schema = HttpPartSchema.create(Query.class, pi);
 		Class<? extends HttpPartParser> pp = schema.getParser();
@@ -112,6 +114,6 @@ public class QueryArg implements RestOpArg {
 			return req.getBeanSession().convertToType(m, cm);
 		}
 
-		return rh.getLast(name).parser(ps).schema(schema).as(type.innerType()).orElse(null);
+		return rh.getLast(name).parser(ps).schema(schema).def(def).as(type.innerType()).orElse(null);
 	}
 }
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestFormParam.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestFormParam.java
index 1e8b914ee..b790a71dc 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestFormParam.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestFormParam.java
@@ -135,6 +135,19 @@ public class RequestFormParam extends RequestHttpPart implements NameValuePair {
 		return value;
 	}
 
+
+	/**
+	 * Sets a default value for this part.
+	 *
+	 * @param def The default value.
+	 * @return This object.
+	 */
+	public RequestFormParam def(String def) {
+		if (getValue() == null)
+			value = def;
+		return this;
+	}
+
 	//------------------------------------------------------------------------------------------------------------------
 	// Assertions
 	//------------------------------------------------------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestHeader.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestHeader.java
index 45459778e..ad147958c 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestHeader.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestHeader.java
@@ -95,7 +95,7 @@ import org.apache.juneau.rest.*;
  */
 public class RequestHeader extends RequestHttpPart implements Header {
 
-	private final String value;
+	private String value;
 
 	/**
 	 * Constructor.
@@ -109,6 +109,18 @@ public class RequestHeader extends RequestHttpPart implements Header {
 		this.value = value;
 	}
 
+	/**
+	 * Sets a default value for this part.
+	 *
+	 * @param def The default value.
+	 * @return This object.
+	 */
+	public RequestHeader def(String def) {
+		if (value == null)
+			value = def;
+		return this;
+	}
+
 	//------------------------------------------------------------------------------------------------------------------
 	// Retrievers
 	//------------------------------------------------------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestPathParam.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestPathParam.java
index 838e390a2..74289917c 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestPathParam.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestPathParam.java
@@ -90,7 +90,7 @@ import org.apache.juneau.rest.*;
  */
 public class RequestPathParam extends RequestHttpPart implements NameValuePair {
 
-	private final String value;
+	private String value;
 
 	/**
 	 * Constructor.
@@ -104,6 +104,18 @@ public class RequestPathParam extends RequestHttpPart implements NameValuePair {
 		this.value = value;
 	}
 
+	/**
+	 * Sets a default value for this part.
+	 *
+	 * @param def The default value.
+	 * @return This object.
+	 */
+	public RequestPathParam def(String def) {
+		if (value == null)
+			value = def;
+		return this;
+	}
+
 	//------------------------------------------------------------------------------------------------------------------
 	// Retrievers
 	//------------------------------------------------------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestQueryParam.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestQueryParam.java
index b094192d1..5c1b8e09c 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestQueryParam.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestQueryParam.java
@@ -90,7 +90,7 @@ import org.apache.juneau.rest.*;
  */
 public class RequestQueryParam extends RequestHttpPart implements NameValuePair {
 
-	private final String value;
+	private String value;
 
 	/**
 	 * Constructor.
@@ -104,6 +104,18 @@ public class RequestQueryParam extends RequestHttpPart implements NameValuePair
 		this.value = value;
 	}
 
+	/**
+	 * Sets a default value for this part.
+	 *
+	 * @param def The default value.
+	 * @return This object.
+	 */
+	public RequestQueryParam def(String def) {
+		if (value == null)
+			value = def;
+		return this;
+	}
+
 	//------------------------------------------------------------------------------------------------------------------
 	// Retrievers
 	//------------------------------------------------------------------------------------------------------------------
diff --git a/juneau-utest/src/test/java/org/apache/juneau/http/annotation/FormDataAnnotation_Test.java b/juneau-utest/src/test/java/org/apache/juneau/http/annotation/FormDataAnnotation_Test.java
index 8919f36e8..78c9ab788 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/http/annotation/FormDataAnnotation_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/http/annotation/FormDataAnnotation_Test.java
@@ -32,6 +32,7 @@ public class FormDataAnnotation_Test {
 	//------------------------------------------------------------------------------------------------------------------
 
 	FormData a1 = FormDataAnnotation.create()
+		.def("def")
 		.name("name")
 		.on("on")
 		.onClass(X1.class)
@@ -41,6 +42,7 @@ public class FormDataAnnotation_Test {
 		.build();
 
 	FormData a2 = FormDataAnnotation.create()
+		.def("def")
 		.name("name")
 		.on("on")
 		.onClass(X1.class)
@@ -53,6 +55,7 @@ public class FormDataAnnotation_Test {
 	public void a01_basic() {
 		assertObject(a1).asJson().isMatches(""
 			+ "{"
+				+ "def:'def',"
 				+ "name:'name',"
 				+ "on:['on'],"
 				+ "onClass:['org.apache.juneau.http.annotation.FormDataAnnotation_Test$X1'],"
@@ -112,6 +115,7 @@ public class FormDataAnnotation_Test {
 	//------------------------------------------------------------------------------------------------------------------
 
 	@FormData(
+		def="def",
 		name="name",
 		on="on",
 		onClass=X1.class,
@@ -123,6 +127,7 @@ public class FormDataAnnotation_Test {
 	FormData d1 = D1.class.getAnnotationsByType(FormData.class)[0];
 
 	@FormData(
+		def="def",
 		name="name",
 		on="on",
 		onClass=X1.class,
diff --git a/juneau-utest/src/test/java/org/apache/juneau/http/annotation/HeaderAnnotation_Test.java b/juneau-utest/src/test/java/org/apache/juneau/http/annotation/HeaderAnnotation_Test.java
index 09c779f21..371c436c0 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/http/annotation/HeaderAnnotation_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/http/annotation/HeaderAnnotation_Test.java
@@ -32,6 +32,7 @@ public class HeaderAnnotation_Test {
 	//------------------------------------------------------------------------------------------------------------------
 
 	Header a1 = HeaderAnnotation.create()
+		.def("def")
 		.name("name")
 		.on("on")
 		.onClass(X1.class)
@@ -41,6 +42,7 @@ public class HeaderAnnotation_Test {
 		.build();
 
 	Header a2 = HeaderAnnotation.create()
+		.def("def")
 		.name("name")
 		.on("on")
 		.onClass(X1.class)
@@ -53,6 +55,7 @@ public class HeaderAnnotation_Test {
 	public void a01_basic() {
 		assertObject(a1).asJson().isMatches(""
 			+ "{"
+				+ "def:'def',"
 				+ "name:'name',"
 				+ "on:['on'],"
 				+ "onClass:['"+CNAME+"$X1'],"
@@ -112,6 +115,7 @@ public class HeaderAnnotation_Test {
 	//------------------------------------------------------------------------------------------------------------------
 
 	@Header(
+		def="def",
 		name="name",
 		on="on",
 		onClass=X1.class,
@@ -123,6 +127,7 @@ public class HeaderAnnotation_Test {
 	Header d1 = D1.class.getAnnotationsByType(Header.class)[0];
 
 	@Header(
+		def="def",
 		name="name",
 		on="on",
 		onClass=X1.class,
diff --git a/juneau-utest/src/test/java/org/apache/juneau/http/annotation/PathAnnotation_Test.java b/juneau-utest/src/test/java/org/apache/juneau/http/annotation/PathAnnotation_Test.java
index 0f1506f38..797475461 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/http/annotation/PathAnnotation_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/http/annotation/PathAnnotation_Test.java
@@ -32,6 +32,7 @@ public class PathAnnotation_Test {
 	//------------------------------------------------------------------------------------------------------------------
 
 	Path a1 = PathAnnotation.create()
+		.def("def")
 		.name("name")
 		.on("on")
 		.onClass(X1.class)
@@ -41,6 +42,7 @@ public class PathAnnotation_Test {
 		.build();
 
 	Path a2 = PathAnnotation.create()
+		.def("def")
 		.name("name")
 		.on("on")
 		.onClass(X1.class)
@@ -53,6 +55,7 @@ public class PathAnnotation_Test {
 	public void a01_basic() {
 		assertObject(a1).asJson().isMatches(""
 			+ "{"
+				+ "def:'def',"
 				+ "name:'name',"
 				+ "on:['on'],"
 				+ "onClass:['"+CNAME+"$X1'],"
@@ -112,6 +115,7 @@ public class PathAnnotation_Test {
 	//------------------------------------------------------------------------------------------------------------------
 
 	@Path(
+		def="def",
 		name="name",
 		on="on",
 		onClass=X1.class,
@@ -123,6 +127,7 @@ public class PathAnnotation_Test {
 	Path d1 = D1.class.getAnnotationsByType(Path.class)[0];
 
 	@Path(
+		def="def",
 		name="name",
 		on="on",
 		onClass=X1.class,
diff --git a/juneau-utest/src/test/java/org/apache/juneau/http/annotation/QueryAnnotation_Test.java b/juneau-utest/src/test/java/org/apache/juneau/http/annotation/QueryAnnotation_Test.java
index 40afced92..7256e7cb4 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/http/annotation/QueryAnnotation_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/http/annotation/QueryAnnotation_Test.java
@@ -32,6 +32,7 @@ public class QueryAnnotation_Test {
 	//------------------------------------------------------------------------------------------------------------------
 
 	Query a1 = QueryAnnotation.create()
+		.def("def")
 		.name("name")
 		.on("on")
 		.onClass(X1.class)
@@ -41,6 +42,7 @@ public class QueryAnnotation_Test {
 		.build();
 
 	Query a2 = QueryAnnotation.create()
+		.def("def")
 		.name("name")
 		.on("on")
 		.onClass(X1.class)
@@ -53,6 +55,7 @@ public class QueryAnnotation_Test {
 	public void a01_basic() {
 		assertObject(a1).asJson().isMatches(""
 			+ "{"
+				+ "def:'def',"
 				+ "name:'name',"
 				+ "on:['on'],"
 				+ "onClass:['"+CNAME+"$X1'],"
@@ -112,6 +115,7 @@ public class QueryAnnotation_Test {
 	//------------------------------------------------------------------------------------------------------------------
 
 	@Query(
+		def="def",
 		name="name",
 		on="on",
 		onClass=X1.class,
@@ -123,6 +127,7 @@ public class QueryAnnotation_Test {
 	Query d1 = D1.class.getAnnotationsByType(Query.class)[0];
 
 	@Query(
+		def="def",
 		name="name",
 		on="on",
 		onClass=X1.class,
diff --git a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/FormData_Test.java b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/FormData_Test.java
index 80f614739..d5a572046 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/FormData_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/FormData_Test.java
@@ -211,4 +211,112 @@ public class FormData_Test {
 			.assertCode().is(200)
 			.assertContent().is("null");
 	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Default form data parameter.
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Rest(serializers=SimpleJsonSerializer.class)
+	public static class F {
+		@RestPost
+		public Object a1(@FormData(name="f1",def="1") Integer f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestPost
+		public Object a2(@FormData(name="f1",def="1") Optional<Integer> f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestPost
+		public Object b1(@FormData(name="f1",def="a=2,b=bar") ABean f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestPost
+		public Object b2(@FormData(name="f1",def="a=2,b=bar") Optional<ABean> f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestPost
+		public Object c1(@FormData(name="f1",def="@((a=2,b=bar))") List<ABean> f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestPost
+		public Object c2(@FormData(name="f1",def="@((a=2,b=bar))") Optional<List<ABean>> f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestPost
+		public Object d(@FormData(name="f1",def="@((a=2,b=bar))") List<Optional<ABean>> f1) throws Exception {
+			return f1;
+		}
+	}
+
+	@Test
+	public void f01_defaultParams() throws Exception {
+		RestClient f = MockRestClient.create(F.class).accept("application/json").contentType("application/x-www-form-urlencoded").build();
+
+		f.post("/a1", "f1=123")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("123");
+		f.post("/a1", "")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("1");
+		f.post("/a2", "f1=123")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("123");
+		f.post("/a2", "")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("1");
+
+		f.post("/b1", "f1=a=1,b=foo")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:1,b:'foo'}");
+		f.post("/b1", "")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:2,b:'bar'}");
+		f.post("/b2", "f1=a=1,b=foo")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:1,b:'foo'}");
+		f.post("/b2", "")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:2,b:'bar'}");
+
+		f.post("/c1", "f1=@((a=1,b=foo))")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:1,b:'foo'}]");
+		f.post("/c1", "null")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:2,b:'bar'}]");
+		f.post("/c2", "f1=@((a=1,b=foo))")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:1,b:'foo'}]");
+		f.post("/c2", "null")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:2,b:'bar'}]");
+
+
+		f.post("/d", "f1=@((a=1,b=foo))")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:1,b:'foo'}]");
+		f.post("/d", "null")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:2,b:'bar'}]");
+	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Header_Test.java b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Header_Test.java
index 9d589f3bb..082f789e6 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Header_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Header_Test.java
@@ -210,4 +210,114 @@ public class Header_Test {
 		e.get("/a").header("H1",7).header("H2",8).header("H3",9).run().assertContent().is("{h1:'7',h2:'8',h3:'9'}");
 		e.get("/a").header("h1",7).header("h2",8).header("h3",9).run().assertContent().is("{h1:'7',h2:'8',h3:'9'}");
 	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Default parameters
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Rest(serializers=SimpleJsonSerializer.class)
+	public static class F {
+		@RestGet
+		public Object a1(@Header(name="f1", def="1") Integer f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestGet
+		public Object a2(@Header(name="f1", def="1") Optional<Integer> f1) throws Exception {
+			assertNotNull(f1);
+			return f1.get();
+		}
+		@RestGet
+		public Object b1(@Header(name="f1", def="a=1,b=foo") ABean f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestGet
+		public Object b2(@Header(name="f1", def="a=1,b=foo") Optional<ABean> f1) throws Exception {
+			assertNotNull(f1);
+			return f1.get();
+		}
+		@RestGet
+		public Object c1(@Header(name="f1", def="@((a=1,b=foo))") List<ABean> f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestGet
+		public Object c2(@Header(name="f1", def="@((a=1,b=foo))") Optional<List<ABean>> f1) throws Exception {
+			assertNotNull(f1);
+			return f1.get();
+		}
+		@RestGet
+		public Object d(@Header(name="f1", def="@((a=1,b=foo))") List<Optional<ABean>> f1) throws Exception {
+			return f1;
+		}
+	}
+
+	@Test
+	public void f01_defaultHeaders() throws Exception {
+		RestClient f = MockRestClient.buildJson(F.class);
+		f.get("/a1")
+			.header("f1","123")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("123");
+		f.get("/a1")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("1");
+		f.get("/a2")
+			.header("f1","123")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("123");
+		f.get("/a2")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("1");
+		f.get("/b1")
+			.header("f1","a=2,b=bar")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:2,b:'bar'}");
+		f.get("/b1")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:1,b:'foo'}");
+		f.get("/b2")
+			.header("f1","a=2,b=bar")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:2,b:'bar'}");
+		f.get("/b2")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:1,b:'foo'}");
+		f.get("/c1")
+			.header("f1","@((a=2,b=bar))")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:2,b:'bar'}]");
+		f.get("/c1")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:1,b:'foo'}]");
+		f.get("/c2")
+			.header("f1","@((a=2,b=bar))")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:2,b:'bar'}]");
+		f.get("/c2")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:1,b:'foo'}]");
+		f.get("/d")
+			.header("f1","@((a=2,b=bar))")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:2,b:'bar'}]");
+		f.get("/d")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:1,b:'foo'}]");
+	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Query_Test.java b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Query_Test.java
index 82b5b9df6..170332cc2 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Query_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Query_Test.java
@@ -288,4 +288,107 @@ public class Query_Test {
 			.assertCode().is(200)
 			.assertContent().is("null");
 	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Default parameters
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Rest(serializers=SimpleJsonSerializer.class)
+	public static class F {
+		@RestGet
+		public Object a1(@Query(name="f1", def="1") Integer f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestGet
+		public Object a2(@Query(name="f1", def="1") Optional<Integer> f1) throws Exception {
+			assertNotNull(f1);
+			return f1.get();
+		}
+		@RestGet
+		public Object b1(@Query(name="f1", def="a=1,b=foo") ABean f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestGet
+		public Object b2(@Query(name="f1", def="a=1,b=foo") Optional<ABean> f1) throws Exception {
+			assertNotNull(f1);
+			return f1.get();
+		}
+		@RestGet
+		public Object c1(@Query(name="f1", def="@((a=1,b=foo))") List<ABean> f1) throws Exception {
+			assertNotNull(f1);
+			return f1;
+		}
+		@RestGet
+		public Object c2(@Query(name="f1", def="@((a=1,b=foo))") Optional<List<ABean>> f1) throws Exception {
+			assertNotNull(f1);
+			return f1.get();
+		}
+		@RestGet
+		public Object d(@Query(name="f1", def="@((a=1,b=foo))") List<Optional<ABean>> f1) throws Exception {
+			return f1;
+		}
+	}
+
+	@Test
+	public void f01_defaultParams() throws Exception {
+		RestClient f = MockRestClient.buildJson(F.class);
+		f.get("/a1?f1=123")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("123");
+		f.get("/a1")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("1");
+		f.get("/a2?f1=123")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("123");
+		f.get("/a2")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("1");
+		f.get("/b1?f1=a=2,b=bar")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:2,b:'bar'}");
+		f.get("/b1")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:1,b:'foo'}");
+		f.get("/b2?f1=a=2,b=bar")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:2,b:'bar'}");
+		f.get("/b2")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("{a:1,b:'foo'}");
+		f.get("/c1?f1=@((a=2,b=bar))")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:2,b:'bar'}]");
+		f.get("/c1")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:1,b:'foo'}]");
+		f.get("/c2?f1=@((a=2,b=bar))")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:2,b:'bar'}]");
+		f.get("/c2")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:1,b:'foo'}]");
+		f.get("/d?f1=@((a=2,b=bar))")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:2,b:'bar'}]");
+		f.get("/d")
+			.run()
+			.assertCode().is(200)
+			.assertContent().is("[{a:1,b:'foo'}]");
+	}
 }