diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java index 81352b361c..8283043945 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java @@ -43,6 +43,7 @@ import org.springframework.web.util.HtmlUtils; * * @author Rob Harrop * @author Juergen Hoeller + * @author Scott Andrews * @since 2.0 * @see org.springframework.web.servlet.mvc.SimpleFormController */ @@ -64,6 +65,12 @@ public class FormTag extends AbstractHtmlElementTag { public static final String MODEL_ATTRIBUTE_VARIABLE_NAME = Conventions.getQualifiedAttributeName(AbstractFormTag.class, MODEL_ATTRIBUTE); + /** Default method parameter, i.e. _method. */ + private static final String DEFAULT_METHOD_PARAM = "_method"; + + private static final String FORM_TAG = "form"; + + private static final String INPUT_TAG = "input"; private static final String ACTION_ATTRIBUTE = "action"; @@ -81,6 +88,12 @@ public class FormTag extends AbstractHtmlElementTag { private static final String AUTOCOMPLETE_ATTRIBUTE = "autocomplete"; + private static final String NAME_ATTRIBUTE = "name"; + + private static final String VALUE_ATTRIBUTE = "value"; + + private static final String TYPE_ATTRIBUTE = "type"; + private TagWriter tagWriter; @@ -104,6 +117,8 @@ public class FormTag extends AbstractHtmlElementTag { private String autocomplete; + private String methodParam = DEFAULT_METHOD_PARAM; + /** Caching a previous nested path, so that it may be reset */ private String previousNestedPath; @@ -278,6 +293,27 @@ public class FormTag extends AbstractHtmlElementTag { return this.autocomplete; } + /** + * Set the name of the request param for non-browser supported HTTP methods + */ + public void setMethodParam(String methodParam) { + this.methodParam = methodParam; + } + + /** + * Get the name of the request param for non-browser supported HTTP methods + */ + protected String getMethodParameter() { + return this.methodParam; + } + + /** + * Determine if the HTTP method is browser supported + */ + protected boolean isMethodBrowserSupported(String method) { + return ("get".equalsIgnoreCase(method) || "post".equalsIgnoreCase(method)); + } + /** @@ -290,10 +326,10 @@ public class FormTag extends AbstractHtmlElementTag { protected int writeTagContent(TagWriter tagWriter) throws JspException { this.tagWriter = tagWriter; - tagWriter.startTag("form"); + tagWriter.startTag(FORM_TAG); writeDefaultAttributes(tagWriter); tagWriter.writeAttribute(ACTION_ATTRIBUTE, resolveAction()); - writeOptionalAttribute(tagWriter, METHOD_ATTRIBUTE, getMethod()); + writeOptionalAttribute(tagWriter, METHOD_ATTRIBUTE, isMethodBrowserSupported(getMethod()) ? getMethod() : DEFAULT_METHOD); writeOptionalAttribute(tagWriter, TARGET_ATTRIBUTE, getTarget()); writeOptionalAttribute(tagWriter, ENCTYPE_ATTRIBUTE, getEnctype()); writeOptionalAttribute(tagWriter, ACCEPT_CHARSET_ATTRIBUTE, getAcceptCharset()); @@ -303,6 +339,14 @@ public class FormTag extends AbstractHtmlElementTag { tagWriter.forceBlock(); + if (!isMethodBrowserSupported(getMethod())) { + tagWriter.startTag(INPUT_TAG); + writeOptionalAttribute(tagWriter, TYPE_ATTRIBUTE, "hidden"); + writeOptionalAttribute(tagWriter, NAME_ATTRIBUTE, getMethodParameter()); + writeOptionalAttribute(tagWriter, VALUE_ATTRIBUTE, getMethod()); + tagWriter.endTag(); + } + // Expose the form object name for nested tags... String modelAttribute = resolveModelAttribute(); this.pageContext.setAttribute(MODEL_ATTRIBUTE_VARIABLE_NAME, modelAttribute, PageContext.REQUEST_SCOPE); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/spring-form.tld b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/spring-form.tld index 76e50863d2..fa16e7a0b5 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/spring-form.tld +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/spring-form.tld @@ -186,6 +186,12 @@ true Common Optional Attribute + + methodParam + false + true + The parameter name used for HTTP methods other then GET and POST. Default is '_method' + diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java index 10c4c5d8ac..892dd373e5 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java @@ -25,6 +25,7 @@ import org.springframework.mock.web.MockHttpServletRequest; * @author Rob Harrop * @author Rick Evans * @author Juergen Hoeller + * @author Scott Andrews */ public class FormTagTests extends AbstractHtmlElementTagTests { @@ -169,6 +170,100 @@ public class FormTagTests extends AbstractHtmlElementTagTests { assertEquals("
", getOutput()); } + + public void testGet() throws Exception { + this.tag.setMethod("get"); + + this.tag.doStartTag(); + this.tag.doEndTag(); + this.tag.doFinally(); + + String output = getOutput(); + String formOutput = getFormTag(output); + String inputOutput = getInputTag(output); + + assertContainsAttribute(formOutput, "method", "get"); + assertEquals("", inputOutput); + } + + public void testPost() throws Exception { + this.tag.setMethod("post"); + + this.tag.doStartTag(); + this.tag.doEndTag(); + this.tag.doFinally(); + + String output = getOutput(); + String formOutput = getFormTag(output); + String inputOutput = getInputTag(output); + + assertContainsAttribute(formOutput, "method", "post"); + assertEquals("", inputOutput); + } + + public void testPut() throws Exception { + this.tag.setMethod("put"); + + this.tag.doStartTag(); + this.tag.doEndTag(); + this.tag.doFinally(); + + String output = getOutput(); + String formOutput = getFormTag(output); + String inputOutput = getInputTag(output); + + assertContainsAttribute(formOutput, "method", "post"); + assertContainsAttribute(inputOutput, "name", "_method"); + assertContainsAttribute(inputOutput, "value", "put"); + assertContainsAttribute(inputOutput, "type", "hidden"); + } + + public void testDelete() throws Exception { + this.tag.setMethod("delete"); + + this.tag.doStartTag(); + this.tag.doEndTag(); + this.tag.doFinally(); + + String output = getOutput(); + String formOutput = getFormTag(output); + String inputOutput = getInputTag(output); + + assertContainsAttribute(formOutput, "method", "post"); + assertContainsAttribute(inputOutput, "name", "_method"); + assertContainsAttribute(inputOutput, "value", "delete"); + assertContainsAttribute(inputOutput, "type", "hidden"); + } + + public void testCustomMethodParameter() throws Exception { + this.tag.setMethod("put"); + this.tag.setMethodParam("methodParameter"); + + this.tag.doStartTag(); + this.tag.doEndTag(); + this.tag.doFinally(); + + String output = getOutput(); + String formOutput = getFormTag(output); + String inputOutput = getInputTag(output); + + assertContainsAttribute(formOutput, "method", "post"); + assertContainsAttribute(inputOutput, "name", "methodParameter"); + assertContainsAttribute(inputOutput, "value", "put"); + assertContainsAttribute(inputOutput, "type", "hidden"); + } + + private String getFormTag(String output) { + int inputStart = output.indexOf("<", 1); + int inputEnd = output.lastIndexOf(">", output.length() - 2); + return output.substring(0, inputStart) + output.substring(inputEnd + 1); + } + + private String getInputTag(String output) { + int inputStart = output.indexOf("<", 1); + int inputEnd = output.lastIndexOf(">", output.length() - 2); + return output.substring(inputStart, inputEnd + 1); + } private static void assertFormTagOpened(String output) {