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:23 UTC

[juneau] branch jbFixRestNpe created (now 848c9e205)

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

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


      at 848c9e205 Add @Query(def) annotation.

This branch includes the following new commits:

     new 848c9e205 Add @Query(def) annotation.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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

Posted by ja...@apache.org.
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'}]");
+	}
 }