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. + * + *

The name of the request parameter defaults to _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