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.
This commit is contained in:
parent
912c1234cd
commit
7d1f2bd1da
|
|
@ -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. <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 {
|
|||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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("<form id=\"command\" action=\"/my/form?foo=bar&stuff="><script>alert('XSS!')</script>\" 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…
Reference in New Issue