From 8de34c6fa7b2e6494b42b6f6a75c7ee10f6d73ed Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 5 Nov 2009 15:42:24 +0000 Subject: [PATCH] SPR-6301 - Support @RequestHeader on HttpHeaders parameters --- .../portlet/context/PortletWebRequest.java | 4 ++ .../ServletAnnotationControllerTests.java | 70 ++++++++++++++++++- .../web/bind/annotation/RequestMapping.java | 12 ++-- .../support/HandlerMethodInvoker.java | 34 +++++++++ .../web/context/request/FacesWebRequest.java | 4 ++ .../context/request/ServletWebRequest.java | 5 ++ .../web/context/request/WebRequest.java | 7 ++ 7 files changed, 131 insertions(+), 5 deletions(-) diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletWebRequest.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletWebRequest.java index 446fa80e323..fc349bf4703 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletWebRequest.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletWebRequest.java @@ -96,6 +96,10 @@ public class PortletWebRequest extends PortletRequestAttributes implements Nativ return (!ObjectUtils.isEmpty(headerValues) ? headerValues : null); } + public Iterator getHeaderNames() { + return CollectionUtils.toIterator(getRequest().getPropertyNames()); + } + public String getParameter(String paramName) { return getRequest().getParameter(paramName); } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java index deb338fcfdc..81d11b218d7 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java @@ -66,6 +66,7 @@ import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.HttpHeaders; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; @@ -1134,6 +1135,31 @@ public class ServletAnnotationControllerTests { assertEquals("key1=[value1],key2=[value21,value22]", response.getContentAsString()); } + @Test + public void requestHeaderMap() throws Exception { + initServlet(RequestHeaderMapController.class); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/map"); + request.addHeader("Content-Type", "text/html"); + request.addHeader("Custom-Header", new String[]{"value21", "value22"}); + MockHttpServletResponse response = new MockHttpServletResponse(); + + servlet.service(request, response); + assertEquals("Content-Type=text/html,Custom-Header=value21", response.getContentAsString()); + + request.setRequestURI("/multiValueMap"); + response = new MockHttpServletResponse(); + + servlet.service(request, response); + assertEquals("Content-Type=[text/html],Custom-Header=[value21,value22]", response.getContentAsString()); + + request.setRequestURI("/httpHeaders"); + response = new MockHttpServletResponse(); + + servlet.service(request, response); + assertEquals("Content-Type=[text/html],Custom-Header=[value21,value22]", response.getContentAsString()); + } + /* * Controllers @@ -1965,6 +1991,48 @@ public class ServletAnnotationControllerTests { } } } - + + @Controller + public static class RequestHeaderMapController { + + @RequestMapping("/map") + public void map(@RequestHeader Map headers, Writer writer) throws IOException { + for (Iterator> it = headers.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + writer.write(entry.getKey() + "=" + entry.getValue()); + if (it.hasNext()) { + writer.write(','); + } + + } + } + + @RequestMapping("/multiValueMap") + public void multiValueMap(@RequestHeader MultiValueMap headers, Writer writer) throws IOException { + for (Iterator>> it1 = headers.entrySet().iterator(); it1.hasNext();) { + Map.Entry> entry = it1.next(); + writer.write(entry.getKey() + "=["); + for (Iterator it2 = entry.getValue().iterator(); it2.hasNext();) { + String value = it2.next(); + writer.write(value); + if (it2.hasNext()) { + writer.write(','); + } + } + writer.write(']'); + if (it1.hasNext()) { + writer.write(','); + } + } + } + + @RequestMapping("/httpHeaders") + public void httpHeaders(@RequestHeader HttpHeaders headers, Writer writer) throws IOException { + assertEquals("Invalid Content-Type", new MediaType("text", "html"), headers.getContentType()); + multiValueMap(headers, writer); + } + + } + } diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java index ce7ff2a48a8..18862228acc 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java +++ b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java @@ -77,12 +77,16 @@ import java.lang.annotation.Target; *
  • {@link RequestParam @RequestParam} annotated parameters for access to * specific Servlet/Portlet request parameters. Parameter values will be * converted to the declared method argument type. Additionally, - * {@code RequestParam @RequestParam} can be used on a {@link java.util.Map Map} or - * {@link org.springframework.util.MultiValueMap MultiValueMap} to gain access - * to all request parameters. + * {@code @RequestParam} can be used on a {@link java.util.Map Map<String, String>} or + * {@link org.springframework.util.MultiValueMap MultiValueMap<String, String>} + * method parameter to gain access to all request parameters. *
  • {@link RequestHeader @RequestHeader} annotated parameters for access to * specific Servlet/Portlet request HTTP headers. Parameter values will be - * converted to the declared method argument type. + * converted to the declared method argument type. Additionally, + * {@code @RequestHeader} can be used on a {@link java.util.Map Map<String, String>}, + * {@link org.springframework.util.MultiValueMap MultiValueMap<String, String>}, or + * {@link org.springframework.http.HttpHeaders HttpHeaders} method parameter to + * gain access to all request headers. *
  • {@link RequestBody @RequestBody} annotated parameters for access to * the Servlet request HTTP contents. Parameter values will be * converted to the declared method argument type using diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java index 87f4941b507..b6213a40021 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java +++ b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.LinkedHashMap; +import java.util.Iterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -41,6 +42,7 @@ import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.HttpInputMessage; import org.springframework.http.MediaType; +import org.springframework.http.HttpHeaders; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; @@ -457,11 +459,15 @@ public class HandlerMethodInvoker { } } + @SuppressWarnings("unchecked") private Object resolveRequestHeader(String headerName, boolean required, String defaultValue, MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall) throws Exception { Class paramType = methodParam.getParameterType(); + if (Map.class.isAssignableFrom(paramType)) { + return resolveRequestHeaderMap((Class) paramType, webRequest); + } if (headerName.length() == 0) { headerName = getRequiredParameterName(methodParam); } @@ -484,6 +490,34 @@ public class HandlerMethodInvoker { return binder.convertIfNecessary(headerValue, paramType, methodParam); } + private Map resolveRequestHeaderMap(Class mapType, NativeWebRequest webRequest) { + if (MultiValueMap.class.isAssignableFrom(mapType)) { + MultiValueMap result; + if (HttpHeaders.class.isAssignableFrom(mapType)) { + result = new HttpHeaders(); + } else { + result = new LinkedMultiValueMap(); + } + for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) { + String headerName = iterator.next(); + for (String headerValue : webRequest.getHeaderValues(headerName)) { + result.add(headerName, headerValue); + } + } + return result; + } + else { + Map result = new LinkedHashMap(); + for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) { + String headerName = iterator.next(); + String headerValue = webRequest.getHeader(headerName); + result.put(headerName, headerValue); + } + return result; + } + } + + /** * Resolves the given {@link RequestBody @RequestBody} annotation. */ diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/request/FacesWebRequest.java b/org.springframework.web/src/main/java/org/springframework/web/context/request/FacesWebRequest.java index 3a387fccb3b..1534ae8f098 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/context/request/FacesWebRequest.java +++ b/org.springframework.web/src/main/java/org/springframework/web/context/request/FacesWebRequest.java @@ -72,6 +72,10 @@ public class FacesWebRequest extends FacesRequestAttributes implements NativeWeb return getExternalContext().getRequestHeaderValuesMap().get(headerName); } + public Iterator getHeaderNames() { + return getExternalContext().getRequestHeaderMap().keySet().iterator(); + } + public String getParameter(String paramName) { return getExternalContext().getRequestParameterMap().get(paramName); } diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java b/org.springframework.web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java index c3745eaf858..25438e48a01 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java +++ b/org.springframework.web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java @@ -102,6 +102,11 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ return (!ObjectUtils.isEmpty(headerValues) ? headerValues : null); } + @SuppressWarnings("unchecked") + public Iterator getHeaderNames() { + return CollectionUtils.toIterator(getRequest().getHeaderNames()); + } + public String getParameter(String paramName) { return getRequest().getParameter(paramName); } diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/request/WebRequest.java b/org.springframework.web/src/main/java/org/springframework/web/context/request/WebRequest.java index dd7e4982351..4690b850094 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/context/request/WebRequest.java +++ b/org.springframework.web/src/main/java/org/springframework/web/context/request/WebRequest.java @@ -47,6 +47,13 @@ public interface WebRequest extends RequestAttributes { */ String[] getHeaderValues(String headerName); + /** + * Return a Iterator over request header names. + * @see javax.servlet.http.HttpServletRequest#getHeaderNames() + * @since 3.0 + */ + Iterator getHeaderNames(); + /** * Return the request parameter of the given name, or null if none. *

    Retrieves the first parameter value in case of a multi-value parameter.