Browse Source

SPR-5267 Support for PUT and DELETE in spring:form tag

Any HTTP method passed to the spring-form:form tag other then GET or POST will automatically be defaulted to POST on the form tag and a hidden input field is created with the requested HTTP method.  By default, the hidden field is named '_method', the name can be overridden using the methodParam attribute on the spring-form:form tag.
conversation
Scott Andrews 16 years ago
parent
commit
7d1f2bd1da
  1. 48
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java
  2. 6
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/spring-form.tld
  3. 95
      org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java

48
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; @@ -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 { @@ -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. <code>_method</code>. */
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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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);

6
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/spring-form.tld

@ -186,6 +186,12 @@ @@ -186,6 +186,12 @@
<rtexprvalue>true</rtexprvalue>
<description>Common Optional Attribute</description>
</attribute>
<attribute>
<name>methodParam</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
<description>The parameter name used for HTTP methods other then GET and POST. Default is '_method'</description>
</attribute>
</tag>
<!-- <form:input/> tag -->

95
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; @@ -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 { @@ -169,6 +170,100 @@ public class FormTagTests extends AbstractHtmlElementTagTests {
assertEquals("<form id=\"command\" action=\"/my/form?foo=bar&amp;stuff=&quot;&gt;&lt;script&gt;alert('XSS!')&lt;/script&gt;\" method=\"post\">",
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) {

Loading…
Cancel
Save