From 16af500388c94bf01700da36791486a9079bd9ac Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Mon, 25 May 2009 10:28:36 +0000 Subject: [PATCH] SPR-5766 - @ResponseBody git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1246 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../web/bind/annotation/RequestBody.java | 17 ++++++++ .../web/bind/annotation/ResponseBody.java | 39 +++++++++++++++++++ .../support/HandlerMethodInvoker.java | 9 ++++- .../AnnotationMethodHandlerAdapter.java | 17 +++++++- .../ServletAnnotationControllerTests.java | 10 +++-- 5 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/ResponseBody.java diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestBody.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestBody.java index 540540fa8c7..ebf36beab64 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestBody.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestBody.java @@ -1,3 +1,19 @@ +/* + * Copyright 2002-2009 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.bind.annotation; import java.lang.annotation.Documented; @@ -12,6 +28,7 @@ import java.lang.annotation.Target; * * @author Arjen Poutsma * @see RequestHeader + * @see ResponseBody * @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter * @since 3.0 */ diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/ResponseBody.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/ResponseBody.java new file mode 100644 index 00000000000..1f274735113 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/ResponseBody.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2009 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.bind.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation which indicates that a method return value should be bound to the web response body. Supported for annotated + * handler methods in Servlet environments. + * + * @author Arjen Poutsma + * @see RequestBody + * @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter + * @since 3.0 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ResponseBody { + +} \ No newline at end of file diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java index 106d9ad712b..2eb9dc5a473 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java @@ -446,7 +446,14 @@ public class HandlerMethodInvoker { Class paramType = methodParam.getParameterType(); MediaType contentType = inputMessage.getHeaders().getContentType(); if (contentType == null) { - throw new HttpMediaTypeNotSupportedException("Cannot extract @RequestBody: no Content-Type found"); + StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType())); + String paramName = methodParam.getParameterName(); + if (paramName != null) { + builder.append(' '); + builder.append(paramName); + } + throw new HttpMediaTypeNotSupportedException( + "Cannot extract @RequestBody parameter (" + builder.toString() + "): no Content-Type found"); } List allSupportedMediaTypes = new ArrayList(); for (HttpMessageConverter messageConverter : messageConverters) { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index 8f11cb0d87d..536af8c5377 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java @@ -50,12 +50,14 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; import org.springframework.http.converter.ByteArrayHttpMessageConverter; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.xml.SourceHttpMessageConverter; import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.util.AntPathMatcher; @@ -76,6 +78,7 @@ import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.annotation.support.HandlerMethodInvoker; @@ -697,7 +700,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen Class handlerType, Object returnValue, ExtendedModelMap implicitModel, - ServletWebRequest webRequest) { + ServletWebRequest webRequest) throws Exception { if (handlerMethod.isAnnotationPresent(ResponseStatus.class)) { ResponseStatus responseStatus = handlerMethod.getAnnotation(ResponseStatus.class); @@ -717,6 +720,18 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } } + if (returnValue != null && handlerMethod.isAnnotationPresent(ResponseBody.class)) { + Class returnValueType = returnValue.getClass(); + HttpOutputMessage outputMessage = new ServletServerHttpResponse(webRequest.getResponse()); + for (HttpMessageConverter messageConverter : messageConverters) { + if (messageConverter.supports(returnValueType)) { + messageConverter.write(returnValue, outputMessage); + responseArgumentUsed = true; + return null; + } + } + } + if (returnValue instanceof ModelAndView) { ModelAndView mav = (ModelAndView) returnValue; mav.getModelMap().mergeAttributes(implicitModel); 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 9620c15b201..72176636880 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 @@ -86,6 +86,7 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.support.WebArgumentResolver; import org.springframework.web.bind.support.WebBindingInitializer; @@ -868,11 +869,11 @@ public class ServletAnnotationControllerTests { } @Test - public void requestBody() throws ServletException, IOException { + public void requestBodyResponseBody() throws ServletException, IOException { initServlet(RequestBodyController.class); MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something"); - String requestBody = "Hello World"; + String requestBody = "HŽll¿ Wšrld"; request.setContent(requestBody.getBytes("UTF-8")); request.addHeader("Content-Type", "text/plain; charset=utf-8"); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -1561,8 +1562,9 @@ public class ServletAnnotationControllerTests { public static class RequestBodyController { @RequestMapping(value = "/something", method = RequestMethod.PUT) - public void handle(@RequestBody String body, Writer writer) throws IOException { - writer.write(body); + @ResponseBody + public String handle(@RequestBody String body) throws IOException { + return body; } }