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. git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@289 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
13fa535939
commit
c76383fc0d
|
|
@ -43,6 +43,7 @@ import org.springframework.web.util.HtmlUtils;
|
||||||
*
|
*
|
||||||
* @author Rob Harrop
|
* @author Rob Harrop
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Scott Andrews
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @see org.springframework.web.servlet.mvc.SimpleFormController
|
* @see org.springframework.web.servlet.mvc.SimpleFormController
|
||||||
*/
|
*/
|
||||||
|
|
@ -64,6 +65,12 @@ public class FormTag extends AbstractHtmlElementTag {
|
||||||
public static final String MODEL_ATTRIBUTE_VARIABLE_NAME =
|
public static final String MODEL_ATTRIBUTE_VARIABLE_NAME =
|
||||||
Conventions.getQualifiedAttributeName(AbstractFormTag.class, MODEL_ATTRIBUTE);
|
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";
|
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 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;
|
private TagWriter tagWriter;
|
||||||
|
|
||||||
|
|
@ -104,6 +117,8 @@ public class FormTag extends AbstractHtmlElementTag {
|
||||||
|
|
||||||
private String autocomplete;
|
private String autocomplete;
|
||||||
|
|
||||||
|
private String methodParam = DEFAULT_METHOD_PARAM;
|
||||||
|
|
||||||
/** Caching a previous nested path, so that it may be reset */
|
/** Caching a previous nested path, so that it may be reset */
|
||||||
private String previousNestedPath;
|
private String previousNestedPath;
|
||||||
|
|
||||||
|
|
@ -278,6 +293,27 @@ public class FormTag extends AbstractHtmlElementTag {
|
||||||
return this.autocomplete;
|
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 {
|
protected int writeTagContent(TagWriter tagWriter) throws JspException {
|
||||||
this.tagWriter = tagWriter;
|
this.tagWriter = tagWriter;
|
||||||
|
|
||||||
tagWriter.startTag("form");
|
tagWriter.startTag(FORM_TAG);
|
||||||
writeDefaultAttributes(tagWriter);
|
writeDefaultAttributes(tagWriter);
|
||||||
tagWriter.writeAttribute(ACTION_ATTRIBUTE, resolveAction());
|
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, TARGET_ATTRIBUTE, getTarget());
|
||||||
writeOptionalAttribute(tagWriter, ENCTYPE_ATTRIBUTE, getEnctype());
|
writeOptionalAttribute(tagWriter, ENCTYPE_ATTRIBUTE, getEnctype());
|
||||||
writeOptionalAttribute(tagWriter, ACCEPT_CHARSET_ATTRIBUTE, getAcceptCharset());
|
writeOptionalAttribute(tagWriter, ACCEPT_CHARSET_ATTRIBUTE, getAcceptCharset());
|
||||||
|
|
@ -303,6 +339,14 @@ public class FormTag extends AbstractHtmlElementTag {
|
||||||
|
|
||||||
tagWriter.forceBlock();
|
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...
|
// Expose the form object name for nested tags...
|
||||||
String modelAttribute = resolveModelAttribute();
|
String modelAttribute = resolveModelAttribute();
|
||||||
this.pageContext.setAttribute(MODEL_ATTRIBUTE_VARIABLE_NAME, modelAttribute, PageContext.REQUEST_SCOPE);
|
this.pageContext.setAttribute(MODEL_ATTRIBUTE_VARIABLE_NAME, modelAttribute, PageContext.REQUEST_SCOPE);
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,12 @@
|
||||||
<rtexprvalue>true</rtexprvalue>
|
<rtexprvalue>true</rtexprvalue>
|
||||||
<description>Common Optional Attribute</description>
|
<description>Common Optional Attribute</description>
|
||||||
</attribute>
|
</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>
|
</tag>
|
||||||
|
|
||||||
<!-- <form:input/> tag -->
|
<!-- <form:input/> tag -->
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
* @author Rob Harrop
|
* @author Rob Harrop
|
||||||
* @author Rick Evans
|
* @author Rick Evans
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Scott Andrews
|
||||||
*/
|
*/
|
||||||
public class FormTagTests extends AbstractHtmlElementTagTests {
|
public class FormTagTests extends AbstractHtmlElementTagTests {
|
||||||
|
|
||||||
|
|
@ -169,6 +170,100 @@ public class FormTagTests extends AbstractHtmlElementTagTests {
|
||||||
assertEquals("<form id=\"command\" action=\"/my/form?foo=bar&stuff="><script>alert('XSS!')</script>\" method=\"post\">",
|
assertEquals("<form id=\"command\" action=\"/my/form?foo=bar&stuff="><script>alert('XSS!')</script>\" method=\"post\">",
|
||||||
getOutput());
|
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) {
|
private static void assertFormTagOpened(String output) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue