Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5b12503

Browse files
committedSep 25, 2009
SPR-6144 - @ResponseStatus annotation is ignored in an @controller redirect (RedirectView)
SPR-5468 - Modify RedirectView to allow 301 Permanent Redirects
1 parent 6fe0e36 commit 5b12503

File tree

5 files changed

+128
-26
lines changed

5 files changed

+128
-26
lines changed
 

‎org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/View.java

+9
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,20 @@
3737
* As this interface is stateless, view implementations should be thread-safe.
3838
*
3939
* @author Rod Johnson
40+
* @author Arjen Poutsma
4041
* @see org.springframework.web.servlet.view.AbstractView
4142
* @see org.springframework.web.servlet.view.InternalResourceView
4243
*/
4344
public interface View {
4445

46+
/**
47+
* Name of the {@link HttpServletRequest} attribute that contains the response status code.
48+
* <p>Note: This attribute is not required to be supported by all
49+
* View implementations.
50+
*/
51+
String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
52+
53+
4554
/**
4655
* Return the content type of the view, if predetermined.
4756
* <p>Can be used to check the content type upfront,

‎org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.springframework.http.HttpInputMessage;
5858
import org.springframework.http.HttpOutputMessage;
5959
import org.springframework.http.MediaType;
60+
import org.springframework.http.HttpStatus;
6061
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
6162
import org.springframework.http.converter.FormHttpMessageConverter;
6263
import org.springframework.http.converter.HttpMessageConverter;
@@ -708,10 +709,12 @@ public ModelAndView getModelAndView(Method handlerMethod,
708709
ExtendedModelMap implicitModel,
709710
ServletWebRequest webRequest) throws Exception {
710711

711-
ResponseStatus responseStatus = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
712-
if (responseStatus != null) {
713-
HttpServletResponse response = webRequest.getResponse();
714-
response.setStatus(responseStatus.value().value());
712+
ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
713+
if (responseStatusAnn != null) {
714+
HttpStatus responseStatus = responseStatusAnn.value();
715+
// to be picked up by the RedirectView
716+
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);
717+
webRequest.getResponse().setStatus(responseStatus.value());
715718
responseArgumentUsed = true;
716719
}
717720

‎org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/RedirectView.java

+34-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import org.springframework.beans.BeanUtils;
3333
import org.springframework.util.ObjectUtils;
3434
import org.springframework.web.util.WebUtils;
35+
import org.springframework.web.servlet.View;
36+
import org.springframework.http.HttpStatus;
3537

3638
/**
3739
* <p>View that redirects to an absolute, context relative, or current request
@@ -63,6 +65,7 @@
6365
* @author Juergen Hoeller
6466
* @author Colin Sampaleanu
6567
* @author Sam Brannen
68+
* @author Arjen Poutsma
6669
* @see #setContextRelative
6770
* @see #setHttp10Compatible
6871
* @see #setExposeModelAttributes
@@ -78,6 +81,8 @@ public class RedirectView extends AbstractUrlBasedView {
7881

7982
private String encodingScheme;
8083

84+
private HttpStatus statusCode;
85+
8186

8287
/**
8388
* Constructor for use as a bean.
@@ -183,6 +188,14 @@ public void setEncodingScheme(String encodingScheme) {
183188
this.encodingScheme = encodingScheme;
184189
}
185190

191+
/**
192+
* Set the status code for this view.
193+
* <p>Default is to send 302/303, depending on the value of the
194+
* {@link #setHttp10Compatible(boolean) http10Compatible} flag.
195+
*/
196+
public void setStatusCode(HttpStatus statusCode) {
197+
this.statusCode = statusCode;
198+
}
186199

187200
/**
188201
* Convert model to request parameters and redirect to the given URL.
@@ -381,10 +394,29 @@ protected void sendRedirect(
381394
response.sendRedirect(response.encodeRedirectURL(targetUrl));
382395
}
383396
else {
384-
// Correct HTTP status code is 303, in particular for POST requests.
385-
response.setStatus(303);
397+
HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl);
398+
response.setStatus(statusCode.value());
386399
response.setHeader("Location", response.encodeRedirectURL(targetUrl));
387400
}
388401
}
389402

403+
/**
404+
* Determines the status code to use for HTTP 1.1 compatible requests.
405+
* <p>The default implemenetation returns the {@link #setStatusCode(HttpStatus) statusCode}
406+
* property if set, or the value of the {@link #RESPONSE_STATUS_ATTRIBUTE} attribute. If neither are
407+
* set, it defaults to {@link HttpStatus#SEE_OTHER} (303).
408+
* @param request the request to inspect
409+
* @return the response
410+
*/
411+
protected HttpStatus getHttp11StatusCode(HttpServletRequest request, HttpServletResponse response, String targetUrl) {
412+
if (statusCode != null) {
413+
return statusCode;
414+
}
415+
HttpStatus attributeStatusCode = (HttpStatus) request.getAttribute(View.RESPONSE_STATUS_ATTRIBUTE);
416+
if (attributeStatusCode != null) {
417+
return attributeStatusCode;
418+
}
419+
return HttpStatus.SEE_OTHER;
420+
}
421+
390422
}

‎org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java

+21
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
import org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver;
118118
import org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping;
119119
import org.springframework.web.servlet.view.InternalResourceViewResolver;
120+
import org.springframework.web.servlet.view.RedirectView;
120121
import org.springframework.web.util.NestedServletException;
121122

122123
/**
@@ -1028,6 +1029,16 @@ public void responseStatus() throws ServletException, IOException {
10281029
assertEquals(201, response.getStatus());
10291030
}
10301031

1032+
@Test
1033+
public void responseStatusRedirect() throws ServletException, IOException {
1034+
initServlet(ResponseStatusRedirectController.class);
1035+
1036+
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/something");
1037+
MockHttpServletResponse response = new MockHttpServletResponse();
1038+
servlet.service(request, response);
1039+
assertEquals(201, response.getStatus());
1040+
}
1041+
10311042
@Test
10321043
public void mavResolver() throws ServletException, IOException {
10331044
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
@@ -1758,6 +1769,16 @@ public void handle(Writer writer) throws IOException {
17581769
}
17591770
}
17601771

1772+
@Controller
1773+
public static class ResponseStatusRedirectController {
1774+
1775+
@RequestMapping("/something")
1776+
@ResponseStatus(HttpStatus.CREATED)
1777+
public RedirectView handle(Writer writer) throws IOException {
1778+
return new RedirectView("somelocation.html", false, false);
1779+
}
1780+
}
1781+
17611782

17621783
@Controller
17631784
public static class ModelAndViewResolverController {

‎org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/RedirectViewTests.java

+57-20
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,15 @@
2828
import junit.framework.AssertionFailedError;
2929
import junit.framework.TestCase;
3030
import org.easymock.MockControl;
31+
import static org.easymock.EasyMock.*;
32+
import org.junit.Test;
33+
import static org.junit.Assert.*;
3134

3235
import org.springframework.beans.TestBean;
3336
import org.springframework.mock.web.MockHttpServletRequest;
3437
import org.springframework.mock.web.MockHttpServletResponse;
38+
import org.springframework.http.HttpStatus;
39+
import org.springframework.web.servlet.View;
3540

3641
/**
3742
* Tests for redirect view, and query string construction.
@@ -40,22 +45,19 @@
4045
* @author Rod Johnson
4146
* @author Juergen Hoeller
4247
* @author Sam Brannen
48+
* @author Arjen Poutsma
4349
* @since 27.05.2003
4450
*/
45-
public class RedirectViewTests extends TestCase {
51+
public class RedirectViewTests {
4652

47-
public void testNoUrlSet() throws Exception {
53+
@Test(expected = IllegalArgumentException.class)
54+
public void noUrlSet() throws Exception {
4855
RedirectView rv = new RedirectView();
49-
try {
50-
rv.afterPropertiesSet();
51-
fail("Should have thrown IllegalArgumentException");
52-
}
53-
catch (IllegalArgumentException ex) {
54-
// expected
55-
}
56+
rv.afterPropertiesSet();
5657
}
5758

58-
public void testHttp11() throws Exception {
59+
@Test
60+
public void http11() throws Exception {
5961
RedirectView rv = new RedirectView();
6062
rv.setUrl("http://url.somewhere.com");
6163
rv.setHttp10Compatible(false);
@@ -66,17 +68,46 @@ public void testHttp11() throws Exception {
6668
assertEquals("http://url.somewhere.com", response.getHeader("Location"));
6769
}
6870

69-
public void testEmptyMap() throws Exception {
71+
@Test
72+
public void explicitStatusCode() throws Exception {
73+
RedirectView rv = new RedirectView();
74+
rv.setUrl("http://url.somewhere.com");
75+
rv.setHttp10Compatible(false);
76+
rv.setStatusCode(HttpStatus.CREATED);
77+
MockHttpServletRequest request = new MockHttpServletRequest();
78+
MockHttpServletResponse response = new MockHttpServletResponse();
79+
rv.render(new HashMap(), request, response);
80+
assertEquals(201, response.getStatus());
81+
assertEquals("http://url.somewhere.com", response.getHeader("Location"));
82+
}
83+
84+
@Test
85+
public void attributeStatusCode() throws Exception {
86+
RedirectView rv = new RedirectView();
87+
rv.setUrl("http://url.somewhere.com");
88+
rv.setHttp10Compatible(false);
89+
MockHttpServletRequest request = new MockHttpServletRequest();
90+
request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.CREATED);
91+
MockHttpServletResponse response = new MockHttpServletResponse();
92+
rv.render(new HashMap(), request, response);
93+
assertEquals(201, response.getStatus());
94+
assertEquals("http://url.somewhere.com", response.getHeader("Location"));
95+
}
96+
97+
@Test
98+
public void emptyMap() throws Exception {
7099
String url = "/myUrl";
71100
doTest(new HashMap(), url, false, url);
72101
}
73102

74-
public void testEmptyMapWithContextRelative() throws Exception {
103+
@Test
104+
public void emptyMapWithContextRelative() throws Exception {
75105
String url = "/myUrl";
76106
doTest(new HashMap(), url, true, url);
77107
}
78108

79-
public void testSingleParam() throws Exception {
109+
@Test
110+
public void singleParam() throws Exception {
80111
String url = "http://url.somewhere.com";
81112
String key = "foo";
82113
String val = "bar";
@@ -86,7 +117,8 @@ public void testSingleParam() throws Exception {
86117
doTest(model, url, false, expectedUrlForEncoding);
87118
}
88119

89-
public void testSingleParamWithoutExposingModelAttributes() throws Exception {
120+
@Test
121+
public void singleParamWithoutExposingModelAttributes() throws Exception {
90122
String url = "http://url.somewhere.com";
91123
String key = "foo";
92124
String val = "bar";
@@ -96,7 +128,8 @@ public void testSingleParamWithoutExposingModelAttributes() throws Exception {
96128
doTest(model, url, false, false, expectedUrlForEncoding);
97129
}
98130

99-
public void testParamWithAnchor() throws Exception {
131+
@Test
132+
public void paramWithAnchor() throws Exception {
100133
String url = "http://url.somewhere.com/test.htm#myAnchor";
101134
String key = "foo";
102135
String val = "bar";
@@ -106,7 +139,8 @@ public void testParamWithAnchor() throws Exception {
106139
doTest(model, url, false, expectedUrlForEncoding);
107140
}
108141

109-
public void testTwoParams() throws Exception {
142+
@Test
143+
public void twoParams() throws Exception {
110144
String url = "http://url.somewhere.com";
111145
String key = "foo";
112146
String val = "bar";
@@ -126,7 +160,8 @@ public void testTwoParams() throws Exception {
126160
}
127161
}
128162

129-
public void testArrayParam() throws Exception {
163+
@Test
164+
public void arrayParam() throws Exception {
130165
String url = "http://url.somewhere.com";
131166
String key = "foo";
132167
String[] val = new String[] {"bar", "baz"};
@@ -143,7 +178,8 @@ public void testArrayParam() throws Exception {
143178
}
144179
}
145180

146-
public void testCollectionParam() throws Exception {
181+
@Test
182+
public void collectionParam() throws Exception {
147183
String url = "http://url.somewhere.com";
148184
String key = "foo";
149185
List val = new ArrayList();
@@ -162,7 +198,8 @@ public void testCollectionParam() throws Exception {
162198
}
163199
}
164200

165-
public void testObjectConversion() throws Exception {
201+
@Test
202+
public void objectConversion() throws Exception {
166203
String url = "http://url.somewhere.com";
167204
String key = "foo";
168205
String val = "bar";
@@ -183,7 +220,7 @@ private void doTest(Map map, String url, boolean contextRelative, String expecte
183220
doTest(map, url, contextRelative, true, expectedUrlForEncoding);
184221
}
185222

186-
private void doTest(final Map map, final String url, final boolean contextRelative,
223+
private void doTest(final Map<String, ?> map, final String url, final boolean contextRelative,
187224
final boolean exposeModelAttributes, String expectedUrlForEncoding) throws Exception {
188225

189226
class TestRedirectView extends RedirectView {

0 commit comments

Comments
 (0)
Please sign in to comment.