Skip to content

Commit 7df2576

Browse files
snicollrstoyanchev
authored andcommitted
Allow HttpHeaders return values for @MVC methods
Allow HttpHeader instances to be returned directly from MVC controller methods managed by HandlerMethodReturnValueHandler rather than needing to be wrapped in a ResponseEntity. Issue: SPR-11129
1 parent b92e429 commit 7df2576

File tree

3 files changed

+101
-0
lines changed

3 files changed

+101
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2002-2014 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.servlet.mvc.method.annotation;
18+
19+
import javax.servlet.http.HttpServletResponse;
20+
21+
import org.springframework.core.MethodParameter;
22+
import org.springframework.http.HttpHeaders;
23+
import org.springframework.http.server.ServletServerHttpResponse;
24+
import org.springframework.util.Assert;
25+
import org.springframework.web.context.request.NativeWebRequest;
26+
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
27+
import org.springframework.web.method.support.ModelAndViewContainer;
28+
29+
/**
30+
* Handles {@link HttpHeaders} return values.
31+
*
32+
* @author Stephane Nicoll
33+
* @since 4.0.1
34+
*/
35+
public class HttpHeadersReturnValueHandler implements HandlerMethodReturnValueHandler {
36+
37+
@Override
38+
public boolean supportsReturnType(MethodParameter returnType) {
39+
return HttpHeaders.class.isAssignableFrom(returnType.getParameterType());
40+
}
41+
42+
@Override
43+
public void handleReturnValue(Object returnValue, MethodParameter returnType,
44+
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
45+
mavContainer.setRequestHandled(true);
46+
47+
Assert.isInstanceOf(HttpHeaders.class, returnValue);
48+
HttpHeaders headers = (HttpHeaders) returnValue;
49+
50+
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
51+
ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
52+
if (!headers.isEmpty()) {
53+
outputMessage.getHeaders().putAll(headers);
54+
}
55+
outputMessage.getBody(); // flush headers
56+
}
57+
}

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
585585
handlers.add(new ModelMethodProcessor());
586586
handlers.add(new ViewMethodReturnValueHandler());
587587
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager));
588+
handlers.add(new HttpHeadersReturnValueHandler());
588589
handlers.add(new CallableMethodReturnValueHandler());
589590
handlers.add(new DeferredResultMethodReturnValueHandler());
590591
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import java.lang.annotation.RetentionPolicy;
2727
import java.lang.annotation.Target;
2828
import java.lang.reflect.Method;
29+
import java.net.URI;
30+
import java.net.URISyntaxException;
2931
import java.security.Principal;
3032
import java.text.SimpleDateFormat;
3133
import java.util.ArrayList;
@@ -1575,6 +1577,28 @@ public void restController() throws Exception {
15751577
assertEquals("Hello World!", response.getContentAsString());
15761578
}
15771579

1580+
@Test
1581+
public void responseAsHttpHeaders() throws Exception {
1582+
initServletWithControllers(HttpHeadersResponseController.class);
1583+
MockHttpServletResponse response = new MockHttpServletResponse();
1584+
getServlet().service(new MockHttpServletRequest("POST", "/"), response);
1585+
1586+
assertEquals("Wrong status code", MockHttpServletResponse.SC_CREATED, response.getStatus());
1587+
assertEquals("Wrong number of headers", 1, response.getHeaderNames().size());
1588+
assertEquals("Wrong value for 'location' header", "/test/items/123", response.getHeader("location"));
1589+
assertEquals("Expected an empty content", 0, response.getContentLength());
1590+
}
1591+
1592+
@Test
1593+
public void responseAsHttpHeadersNoHeader() throws Exception {
1594+
initServletWithControllers(HttpHeadersResponseController.class);
1595+
MockHttpServletResponse response = new MockHttpServletResponse();
1596+
getServlet().service(new MockHttpServletRequest("POST", "/empty"), response);
1597+
1598+
assertEquals("Wrong status code", MockHttpServletResponse.SC_CREATED, response.getStatus());
1599+
assertEquals("Wrong number of headers", 0, response.getHeaderNames().size());
1600+
assertEquals("Expected an empty content", 0, response.getContentLength());
1601+
}
15781602

15791603
/*
15801604
* Controllers
@@ -2995,6 +3019,25 @@ public String home() {
29953019
}
29963020
}
29973021

3022+
@Controller
3023+
static class HttpHeadersResponseController {
3024+
3025+
@RequestMapping(value = "", method = RequestMethod.POST)
3026+
@ResponseStatus(HttpStatus.CREATED)
3027+
public HttpHeaders create() throws URISyntaxException {
3028+
HttpHeaders headers = new HttpHeaders();
3029+
headers.setLocation(new URI("/test/items/123"));
3030+
return headers;
3031+
}
3032+
3033+
@RequestMapping(value = "empty", method = RequestMethod.POST)
3034+
@ResponseStatus(HttpStatus.CREATED)
3035+
public HttpHeaders createNoHeader() throws URISyntaxException {
3036+
return new HttpHeaders();
3037+
}
3038+
3039+
}
3040+
29983041

29993042
// Test cases deleted from the original SevletAnnotationControllerTests:
30003043

0 commit comments

Comments
 (0)