From 7df25764af07d172c210d7207dd4cce5a8a3e123 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Sat, 18 Jan 2014 11:23:32 +0100 Subject: [PATCH] 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 --- .../HttpHeadersReturnValueHandler.java | 57 +++++++++++++++++++ .../RequestMappingHandlerAdapter.java | 1 + ...nnotationControllerHandlerMethodTests.java | 43 ++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpHeadersReturnValueHandler.java diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpHeadersReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpHeadersReturnValueHandler.java new file mode 100644 index 0000000000..e00d00112f --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpHeadersReturnValueHandler.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2014 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.servlet.mvc.method.annotation; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.core.MethodParameter; +import org.springframework.http.HttpHeaders; +import org.springframework.http.server.ServletServerHttpResponse; +import org.springframework.util.Assert; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodReturnValueHandler; +import org.springframework.web.method.support.ModelAndViewContainer; + +/** + * Handles {@link HttpHeaders} return values. + * + * @author Stephane Nicoll + * @since 4.0.1 + */ +public class HttpHeadersReturnValueHandler implements HandlerMethodReturnValueHandler { + + @Override + public boolean supportsReturnType(MethodParameter returnType) { + return HttpHeaders.class.isAssignableFrom(returnType.getParameterType()); + } + + @Override + public void handleReturnValue(Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { + mavContainer.setRequestHandled(true); + + Assert.isInstanceOf(HttpHeaders.class, returnValue); + HttpHeaders headers = (HttpHeaders) returnValue; + + HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); + ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response); + if (!headers.isEmpty()) { + outputMessage.getHeaders().putAll(headers); + } + outputMessage.getBody(); // flush headers + } +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index eb11922001..701cace142 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -585,6 +585,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i handlers.add(new ModelMethodProcessor()); handlers.add(new ViewMethodReturnValueHandler()); handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager)); + handlers.add(new HttpHeadersReturnValueHandler()); handlers.add(new CallableMethodReturnValueHandler()); handlers.add(new DeferredResultMethodReturnValueHandler()); handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory)); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index d9109a2738..87731d3f89 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -26,6 +26,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; import java.security.Principal; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -1575,6 +1577,28 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl assertEquals("Hello World!", response.getContentAsString()); } + @Test + public void responseAsHttpHeaders() throws Exception { + initServletWithControllers(HttpHeadersResponseController.class); + MockHttpServletResponse response = new MockHttpServletResponse(); + getServlet().service(new MockHttpServletRequest("POST", "/"), response); + + assertEquals("Wrong status code", MockHttpServletResponse.SC_CREATED, response.getStatus()); + assertEquals("Wrong number of headers", 1, response.getHeaderNames().size()); + assertEquals("Wrong value for 'location' header", "/test/items/123", response.getHeader("location")); + assertEquals("Expected an empty content", 0, response.getContentLength()); + } + + @Test + public void responseAsHttpHeadersNoHeader() throws Exception { + initServletWithControllers(HttpHeadersResponseController.class); + MockHttpServletResponse response = new MockHttpServletResponse(); + getServlet().service(new MockHttpServletRequest("POST", "/empty"), response); + + assertEquals("Wrong status code", MockHttpServletResponse.SC_CREATED, response.getStatus()); + assertEquals("Wrong number of headers", 0, response.getHeaderNames().size()); + assertEquals("Expected an empty content", 0, response.getContentLength()); + } /* * Controllers @@ -2995,6 +3019,25 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl } } + @Controller + static class HttpHeadersResponseController { + + @RequestMapping(value = "", method = RequestMethod.POST) + @ResponseStatus(HttpStatus.CREATED) + public HttpHeaders create() throws URISyntaxException { + HttpHeaders headers = new HttpHeaders(); + headers.setLocation(new URI("/test/items/123")); + return headers; + } + + @RequestMapping(value = "empty", method = RequestMethod.POST) + @ResponseStatus(HttpStatus.CREATED) + public HttpHeaders createNoHeader() throws URISyntaxException { + return new HttpHeaders(); + } + + } + // Test cases deleted from the original SevletAnnotationControllerTests: -- GitLab