Skip to content

Commit 0ed9164

Browse files
committed
added @RequestHeader support
1 parent b3866a9 commit 0ed9164

File tree

9 files changed

+281
-80
lines changed

9 files changed

+281
-80
lines changed
Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
1-
SPRING FRAMEWORK 3.0.0 M1 ()
2-
----------------------------------
1+
SPRING FRAMEWORK 3.0 M1 (December 2008)
2+
---------------------------------------
33
http://www.springframework.org
44

55
1. INTRODUCTION
66
---------------
77

8+
This is the first milestone of Spring 3.0 which is scheduled for final release in Q2 2009.
9+
This release updates the entire codebase for Java 5+ and introduces EL and REST support.
10+
811
2. RELEASE NOTES
912
----------------
1013

14+
This release comes without reference documentation. For the time being, please consult
15+
the provided javadoc for details on the new features.
16+
1117
3. DISTRIBUTION JAR FILES
1218
-------------------------
1319

20+
The Spring Framework module jar files can be found in the 'dist' directory. Note that
21+
this release does not contain a 'spring.jar' file anymore. Furthermore, the jar file
22+
names follow bundle repository conventions now.
23+
1424
4. GETTING STARTED
1525
------------------
1626

17-
5. OBTAINING RELEASE JARS USING MAVEN OR IVY
18-
--------------------------------------------
19-
20-
6. ADDITIONAL RESOURCES
21-
-----------------------
27+
Check out the provided PetClinic sample application. It has been partially updated
28+
for Spring 3.0 already.

org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletWebRequest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import javax.portlet.PortletResponse;
2424
import javax.portlet.PortletSession;
2525

26+
import org.springframework.util.ObjectUtils;
2627
import org.springframework.util.StringUtils;
2728
import org.springframework.web.context.request.NativeWebRequest;
2829

@@ -73,6 +74,16 @@ public Object getNativeResponse() {
7374
}
7475

7576

77+
public String getHeader(String headerName) {
78+
return getRequest().getProperty(headerName);
79+
}
80+
81+
@SuppressWarnings("unchecked")
82+
public String[] getHeaderValues(String headerName) {
83+
String[] headerValues = StringUtils.toStringArray(getRequest().getProperties(headerName));
84+
return (!ObjectUtils.isEmpty(headerValues) ? headerValues : null);
85+
}
86+
7687
public String getParameter(String paramName) {
7788
return getRequest().getParameter(paramName);
7889
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2002-2008 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.bind.annotation;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
/**
26+
* Annotation which indicates that a method parameter should be bound to a web request header.
27+
* Supported for {@link RequestMapping} annotated handler methods in Servlet and Portlet environments.
28+
*
29+
* @author Juergen Hoeller
30+
* @since 3.0
31+
* @see RequestMapping
32+
* @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
33+
* @see org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter
34+
*/
35+
@Target(ElementType.PARAMETER)
36+
@Retention(RetentionPolicy.RUNTIME)
37+
@Documented
38+
public @interface RequestHeader {
39+
40+
/**
41+
* The name of the request header to bind to.
42+
*/
43+
String value() default "";
44+
45+
/**
46+
* Whether the header is required.
47+
* <p>Default is <code>true</code>, leading to an exception thrown in case
48+
* of the header missing in the request. Switch this to <code>false</code>
49+
* if you prefer a <code>null</value> in case of the header missing.
50+
* <p>Alternatively, provide a {@link #defaultValue() defaultValue},
51+
* which implicitely sets this flag to <code>false</code>.
52+
*/
53+
boolean required() default true;
54+
55+
/**
56+
* The default value to use as a fallback. Supplying a default value implicitely
57+
* sets {@link #required()} to false.
58+
*/
59+
String defaultValue() default "";
60+
61+
}

org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestParam.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2007 the original author or authors.
2+
* Copyright 2002-2008 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,33 +23,40 @@
2323
import java.lang.annotation.Target;
2424

2525
/**
26-
* Annotation which indicates that a method parameter should be bound to a web request parameter. Supported for {@link
27-
* RequestMapping} annotated handler methods in Servlet and Portlet environments.
26+
* Annotation which indicates that a method parameter should be bound to a web request parameter.
27+
* Supported for {@link RequestMapping} annotated handler methods in Servlet and Portlet environments.
2828
*
2929
* @author Arjen Poutsma
3030
* @author Juergen Hoeller
31+
* @since 2.5
3132
* @see RequestMapping
3233
* @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
3334
* @see org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter
34-
* @since 2.5
3535
*/
3636
@Target(ElementType.PARAMETER)
3737
@Retention(RetentionPolicy.RUNTIME)
3838
@Documented
3939
public @interface RequestParam {
4040

41-
/** The request parameter to bind to. */
41+
/**
42+
* The name of the request parameter to bind to.
43+
*/
4244
String value() default "";
4345

4446
/**
45-
* Whether the parameter is required. <p>Default is <code>true</code>, leading to an exception thrown in case of the
46-
* parameter missing in the request. Switch this to <code>false</code> if you prefer a <code>null</value> in case of
47-
* the parameter missing. <p>Alternatively, provide a {@link #defaultValue() defaultValue}, which implicitely sets this
48-
* flag to <code>false</code>.
47+
* Whether the parameter is required.
48+
* <p>Default is <code>true</code>, leading to an exception thrown in case
49+
* of the parameter missing in the request. Switch this to <code>false</code>
50+
* if you prefer a <code>null</value> in case of the parameter missing.
51+
* <p>Alternatively, provide a {@link #defaultValue() defaultValue},
52+
* which implicitely sets this flag to <code>false</code>.
4953
*/
5054
boolean required() default true;
5155

52-
/** The default value to use as a fallback. Supplying a default value implicitely sets {@link #required()} to false. */
56+
/**
57+
* The default value to use as a fallback. Supplying a default value implicitely
58+
* sets {@link #required()} to false.
59+
*/
5360
String defaultValue() default "";
5461

5562
}

org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java

Lines changed: 81 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.springframework.web.bind.annotation.InitBinder;
4747
import org.springframework.web.bind.annotation.ModelAttribute;
4848
import org.springframework.web.bind.annotation.PathVariable;
49+
import org.springframework.web.bind.annotation.RequestHeader;
4950
import org.springframework.web.bind.annotation.RequestParam;
5051
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
5152
import org.springframework.web.bind.support.SessionAttributeStore;
@@ -156,36 +157,47 @@ private Object[] resolveHandlerArguments(Method handlerMethod,
156157
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
157158
GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
158159
String paramName = null;
159-
boolean paramRequired = false;
160-
String paramDefaultValue = null;
160+
String headerName = null;
161+
boolean required = false;
162+
String defaultValue = null;
161163
String pathVarName = null;
162164
String attrName = null;
165+
int found = 0;
163166
Annotation[] paramAnns = methodParam.getParameterAnnotations();
164167

165168
for (Annotation paramAnn : paramAnns) {
166169
if (RequestParam.class.isInstance(paramAnn)) {
167170
RequestParam requestParam = (RequestParam) paramAnn;
168171
paramName = requestParam.value();
169-
paramRequired = requestParam.required();
170-
paramDefaultValue = requestParam.defaultValue();
171-
break;
172+
required = requestParam.required();
173+
defaultValue = requestParam.defaultValue();
174+
found++;
175+
}
176+
else if (RequestHeader.class.isInstance(paramAnn)) {
177+
RequestHeader requestHeader = (RequestHeader) paramAnn;
178+
headerName = requestHeader.value();
179+
required = requestHeader.required();
180+
defaultValue = requestHeader.defaultValue();
181+
found++;
172182
}
173183
else if (ModelAttribute.class.isInstance(paramAnn)) {
174184
ModelAttribute attr = (ModelAttribute) paramAnn;
175185
attrName = attr.value();
186+
found++;
176187
}
177188
else if (PathVariable.class.isInstance(paramAnn)) {
178189
PathVariable pathVar = (PathVariable) paramAnn;
179190
pathVarName = pathVar.value();
191+
found++;
180192
}
181193
}
182-
if ((paramName != null && attrName != null) || (paramName != null && pathVarName != null) ||
183-
(pathVarName != null && attrName != null)) {
184-
throw new IllegalStateException("@RequestParam, @PathVariable and @ModelAttribute are exclusive " +
185-
"choices - do not specify both on the same parameter: " + handlerMethod);
194+
195+
if (found > 1) {
196+
throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
197+
"do not specify more than one such annotation on the same parameter: " + handlerMethod);
186198
}
187199

188-
if (paramName == null && attrName == null && pathVarName == null) {
200+
if (found == 0) {
189201
Object argValue = resolveCommonArgument(methodParam, webRequest);
190202
if (argValue != WebArgumentResolver.UNRESOLVED) {
191203
args[i] = argValue;
@@ -212,8 +224,10 @@ else if (BeanUtils.isSimpleProperty(paramType)) {
212224
}
213225

214226
if (paramName != null) {
215-
args[i] = resolveRequestParam(paramName, paramRequired, paramDefaultValue, methodParam, webRequest,
216-
handler);
227+
args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
228+
}
229+
else if (headerName != null) {
230+
args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
217231
}
218232
else if (attrName != null) {
219233
WebDataBinder binder = resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
@@ -333,18 +347,13 @@ else if (pathVarName != null) {
333347
return initBinderArgs;
334348
}
335349

336-
private Object resolveRequestParam(String paramName, boolean paramRequired, String paramDefaultValue,
350+
private Object resolveRequestParam(String paramName, boolean required, String defaultValue,
337351
MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
338352
throws Exception {
339353

340354
Class paramType = methodParam.getParameterType();
341355
if (paramName.length() == 0) {
342-
paramName = methodParam.getParameterName();
343-
if (paramName == null) {
344-
throw new IllegalStateException(
345-
"No parameter specified for @RequestParam argument of type [" + paramType.getName() +
346-
"], and no parameter name information found in class file either.");
347-
}
356+
paramName = getRequiredParameterName(methodParam);
348357
}
349358
Object paramValue = null;
350359
if (webRequest.getNativeRequest() instanceof MultipartRequest) {
@@ -357,23 +366,64 @@ private Object resolveRequestParam(String paramName, boolean paramRequired, Stri
357366
}
358367
}
359368
if (paramValue == null) {
360-
if (StringUtils.hasText(paramDefaultValue)) {
361-
paramValue = paramDefaultValue;
369+
if (StringUtils.hasText(defaultValue)) {
370+
paramValue = defaultValue;
362371
}
363-
else if (paramRequired) {
372+
else if (required) {
364373
raiseMissingParameterException(paramName, paramType);
365374
}
366-
if (paramValue == null && paramType.isPrimitive()) {
367-
throw new IllegalStateException("Optional " + paramType + " parameter '" + paramName +
368-
"' is not present but cannot be translated into a null value due to being declared as a " +
369-
"primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
370-
}
375+
checkValue(paramName, paramValue, paramType);
371376
}
372377
WebDataBinder binder = createBinder(webRequest, null, paramName);
373378
initBinder(handlerForInitBinderCall, paramName, binder, webRequest);
374379
return binder.convertIfNecessary(paramValue, paramType, methodParam);
375380
}
376381

382+
private Object resolveRequestHeader(String headerName, boolean required, String defaultValue,
383+
MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
384+
throws Exception {
385+
386+
Class paramType = methodParam.getParameterType();
387+
if (headerName.length() == 0) {
388+
headerName = getRequiredParameterName(methodParam);
389+
}
390+
Object headerValue = null;
391+
String[] headerValues = webRequest.getHeaderValues(headerName);
392+
if (headerValues != null) {
393+
headerValue = (headerValues.length == 1 ? headerValues[0] : headerValues);
394+
}
395+
if (headerValue == null) {
396+
if (StringUtils.hasText(defaultValue)) {
397+
headerValue = defaultValue;
398+
}
399+
else if (required) {
400+
raiseMissingHeaderException(headerName, paramType);
401+
}
402+
checkValue(headerName, headerValue, paramType);
403+
}
404+
WebDataBinder binder = createBinder(webRequest, null, headerName);
405+
initBinder(handlerForInitBinderCall, headerName, binder, webRequest);
406+
return binder.convertIfNecessary(headerValue, paramType, methodParam);
407+
}
408+
409+
private String getRequiredParameterName(MethodParameter methodParam) {
410+
String name = methodParam.getParameterName();
411+
if (name == null) {
412+
throw new IllegalStateException("No parameter name specified for argument of type [" +
413+
methodParam.getParameterType().getName() +
414+
"], and no parameter name information found in class file either.");
415+
}
416+
return name;
417+
}
418+
419+
private void checkValue(String name, Object value, Class paramType) {
420+
if (value == null && paramType.isPrimitive()) {
421+
throw new IllegalStateException("Optional " + paramType + " parameter '" + name +
422+
"' is not present but cannot be translated into a null value due to being declared as a " +
423+
"primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
424+
}
425+
}
426+
377427
private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
378428
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler)
379429
throws Exception {
@@ -471,6 +521,10 @@ protected void raiseMissingParameterException(String paramName, Class paramType)
471521
throw new IllegalStateException("Missing parameter '" + paramName + "' of type [" + paramType.getName() + "]");
472522
}
473523

524+
protected void raiseMissingHeaderException(String headerName, Class paramType) throws Exception {
525+
throw new IllegalStateException("Missing header '" + headerName + "' of type [" + paramType.getName() + "]");
526+
}
527+
474528
protected void raiseSessionRequiredException(String message) throws Exception {
475529
throw new IllegalStateException(message);
476530
}

0 commit comments

Comments
 (0)