diff --git a/org.springframework.web/src/main/java/org/springframework/web/filter/HiddenHttpMethodFilter.java b/org.springframework.web/src/main/java/org/springframework/web/filter/HiddenHttpMethodFilter.java
new file mode 100644
index 0000000000..2ec89e6c21
--- /dev/null
+++ b/org.springframework.web/src/main/java/org/springframework/web/filter/HiddenHttpMethodFilter.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.filter;
+
+import java.io.IOException;
+import java.util.Locale;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Servlet 2.3/2.4 {@link javax.servlet.Filter} that converts posted method parameters into HTTP methods, retrievable
+ * via {@link HttpServletRequest#getMethod()}. Since browsers currently only support GET and POST, a common technique -
+ * used by the Prototype library, for instance - is to use a normal POST with an additional hidden form field
+ * (_method
) to pass the "real" HTTP method. This filter reads that parameter, and changes of {@link
+ * HttpServletRequestWrapper#getMethod()} accordingly.
+ *
+ *
_method
, but can be changed via the {@link
+ * #setMethodParam(String) methodParam} property.
+ *
+ * @author Arjen Poutsma
+ * @since 3.0
+ */
+public class HiddenHttpMethodFilter extends OncePerRequestFilter {
+
+ /** Default method parameter, i.e. _method
. */
+ public static final String DEFAULT_METHOD_PARAM = "_method";
+
+ private String methodParam = DEFAULT_METHOD_PARAM;
+
+ /**
+ * Set the parameter name to look for HTTP methods.
+ *
+ * @see #DEFAULT_METHOD_PARAM
+ */
+ public void setMethodParam(String methodParam) {
+ Assert.hasText(methodParam, "'methodParam' must not be empty");
+ this.methodParam = methodParam;
+ }
+
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+ if ("POST".equals(request.getMethod()) && StringUtils.hasLength(request.getParameter(methodParam))) {
+ String method = request.getParameter(methodParam).toUpperCase(Locale.ENGLISH);
+ HttpServletRequest wrapper = new HttpMethodRequestWrapper(method, request);
+ filterChain.doFilter(wrapper, response);
+ }
+ else {
+ filterChain.doFilter(request, response);
+ }
+ }
+
+ /**
+ * Simple {@link HttpServletRequest} wrapper that returns the supplied method for {@link
+ * HttpServletRequest#getMethod()}.
+ */
+ private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
+
+ private final String method;
+
+ private HttpMethodRequestWrapper(String method, HttpServletRequest request) {
+ super(request);
+ this.method = method;
+ }
+
+ public String getMethod() {
+ return method;
+ }
+ }
+}
diff --git a/org.springframework.web/src/test/java/org/springframework/web/filter/HiddenHttpMethodFilterTest.java b/org.springframework.web/src/test/java/org/springframework/web/filter/HiddenHttpMethodFilterTest.java
new file mode 100644
index 0000000000..d9904c9419
--- /dev/null
+++ b/org.springframework.web/src/test/java/org/springframework/web/filter/HiddenHttpMethodFilterTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright ${YEAR} the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.filter;
+
+import java.io.IOException;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+/** @author Arjen Poutsma */
+public class HiddenHttpMethodFilterTest {
+
+ private HiddenHttpMethodFilter filter;
+
+ @Before
+ public void createFilter() {
+ filter = new HiddenHttpMethodFilter();
+ }
+
+ @Test
+ public void filterWithParameter() throws IOException, ServletException {
+ MockHttpServletRequest request = new MockHttpServletRequest("POST", "/hotels");
+ request.addParameter("_method", "delete");
+ MockHttpServletResponse response = new MockHttpServletResponse();
+
+ FilterChain filterChain = new FilterChain() {
+
+ public void doFilter(ServletRequest filterRequest, ServletResponse filterResponse)
+ throws IOException, ServletException {
+ assertEquals("Invalid method", "DELETE", ((HttpServletRequest) filterRequest).getMethod());
+ }
+ };
+ filter.doFilter(request, response, filterChain);
+ }
+
+ @Test
+ public void filterWithNoParameter() throws IOException, ServletException {
+ MockHttpServletRequest request = new MockHttpServletRequest("POST", "/hotels");
+ MockHttpServletResponse response = new MockHttpServletResponse();
+
+ FilterChain filterChain = new FilterChain() {
+
+ public void doFilter(ServletRequest filterRequest, ServletResponse filterResponse)
+ throws IOException, ServletException {
+ assertEquals("Invalid method", "POST", ((HttpServletRequest) filterRequest).getMethod());
+ }
+ };
+ filter.doFilter(request, response, filterChain);
+ }
+}
\ No newline at end of file