Skip to content

Commit dc0613f

Browse files
committed
HttpMessageConverter.supports() is split into canRead/canWrite.
HttpMessageConverter.write() now allows for a specific content type.
1 parent 18c63f7 commit dc0613f

22 files changed

+627
-517
lines changed

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

Lines changed: 51 additions & 72 deletions
Large diffs are not rendered by default.

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

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@
3030
import java.util.Collections;
3131
import java.util.Date;
3232
import java.util.HashSet;
33+
import java.util.Iterator;
3334
import java.util.LinkedList;
3435
import java.util.List;
3536
import java.util.Locale;
3637
import java.util.Map;
3738
import java.util.Set;
38-
import java.util.Iterator;
3939
import javax.servlet.ServletConfig;
4040
import javax.servlet.ServletContext;
4141
import javax.servlet.ServletException;
@@ -52,25 +52,27 @@
5252
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
5353
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
5454
import org.springframework.aop.support.DefaultPointcutAdvisor;
55+
import org.springframework.beans.BeansException;
5556
import org.springframework.beans.DerivedTestBean;
5657
import org.springframework.beans.ITestBean;
5758
import org.springframework.beans.TestBean;
58-
import org.springframework.beans.BeansException;
5959
import org.springframework.beans.factory.annotation.Autowired;
6060
import org.springframework.beans.factory.annotation.Value;
6161
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
6262
import org.springframework.beans.factory.support.RootBeanDefinition;
6363
import org.springframework.beans.propertyeditors.CustomDateEditor;
6464
import org.springframework.context.annotation.AnnotationConfigUtils;
6565
import org.springframework.core.MethodParameter;
66+
import org.springframework.http.HttpHeaders;
6667
import org.springframework.http.HttpInputMessage;
6768
import org.springframework.http.HttpOutputMessage;
6869
import org.springframework.http.HttpStatus;
6970
import org.springframework.http.MediaType;
70-
import org.springframework.http.HttpHeaders;
71+
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
7172
import org.springframework.http.converter.HttpMessageConverter;
7273
import org.springframework.http.converter.HttpMessageNotReadableException;
7374
import org.springframework.http.converter.HttpMessageNotWritableException;
75+
import org.springframework.http.converter.StringHttpMessageConverter;
7476
import org.springframework.mock.web.MockHttpServletRequest;
7577
import org.springframework.mock.web.MockHttpServletResponse;
7678
import org.springframework.mock.web.MockServletConfig;
@@ -79,9 +81,9 @@
7981
import org.springframework.ui.ExtendedModelMap;
8082
import org.springframework.ui.Model;
8183
import org.springframework.ui.ModelMap;
84+
import org.springframework.util.MultiValueMap;
8285
import org.springframework.util.SerializationTestUtils;
8386
import org.springframework.util.StringUtils;
84-
import org.springframework.util.MultiValueMap;
8587
import org.springframework.validation.BindingResult;
8688
import org.springframework.validation.Errors;
8789
import org.springframework.validation.FieldError;
@@ -445,7 +447,8 @@ protected WebApplicationContext createWebApplicationContext(WebApplicationContex
445447
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
446448
autoProxyCreator.setBeanFactory(wac.getBeanFactory());
447449
wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator);
448-
wac.getBeanFactory().registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
450+
wac.getBeanFactory()
451+
.registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
449452
wac.refresh();
450453
return wac;
451454
}
@@ -639,8 +642,8 @@ protected WebApplicationContext createWebApplicationContext(WebApplicationContex
639642
servlet.service(request, response);
640643
assertEquals("mySurpriseView", response.getContentAsString());
641644

642-
MyParameterDispatchingController deserialized = (MyParameterDispatchingController)
643-
SerializationTestUtils.serializeAndDeserialize(servlet.getWebApplicationContext().getBean("controller"));
645+
MyParameterDispatchingController deserialized = (MyParameterDispatchingController) SerializationTestUtils
646+
.serializeAndDeserialize(servlet.getWebApplicationContext().getBean("controller"));
644647
assertNotNull(deserialized.request);
645648
assertNotNull(deserialized.session);
646649
}
@@ -947,7 +950,23 @@ public void requestBodyResponseBody() throws ServletException, IOException {
947950

948951
@Test
949952
public void responseBodyNoAcceptableMediaType() throws ServletException, IOException {
950-
initServlet(RequestBodyController.class);
953+
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
954+
@Override
955+
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
956+
GenericWebApplicationContext wac = new GenericWebApplicationContext();
957+
wac.registerBeanDefinition("controller", new RootBeanDefinition(RequestBodyController.class));
958+
RootBeanDefinition converterDef = new RootBeanDefinition(StringHttpMessageConverter.class);
959+
converterDef.getPropertyValues().add("supportedMediaTypes", new MediaType("text", "plain"));
960+
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
961+
StringHttpMessageConverter converter = new StringHttpMessageConverter();
962+
converter.setSupportedMediaTypes(Collections.singletonList(new MediaType("text", "plain")));
963+
adapterDef.getPropertyValues().add("messageConverters", converter);
964+
wac.registerBeanDefinition("handlerAdapter", adapterDef);
965+
wac.refresh();
966+
return wac;
967+
}
968+
};
969+
servlet.init(new MockServletConfig());
951970

952971
MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
953972
String requestBody = "Hello World";
@@ -975,7 +994,19 @@ public void responseBodyWildCardMediaType() throws ServletException, IOException
975994

976995
@Test
977996
public void unsupportedRequestBody() throws ServletException, IOException {
978-
initServlet(RequestBodyController.class);
997+
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
998+
@Override
999+
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
1000+
GenericWebApplicationContext wac = new GenericWebApplicationContext();
1001+
wac.registerBeanDefinition("controller", new RootBeanDefinition(RequestBodyController.class));
1002+
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
1003+
adapterDef.getPropertyValues().add("messageConverters", new ByteArrayHttpMessageConverter());
1004+
wac.registerBeanDefinition("handlerAdapter", adapterDef);
1005+
wac.refresh();
1006+
return wac;
1007+
}
1008+
};
1009+
servlet.init(new MockServletConfig());
9791010

9801011
MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
9811012
String requestBody = "Hello World";
@@ -1061,11 +1092,9 @@ public void mavResolver() throws ServletException, IOException {
10611092
@Override
10621093
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
10631094
GenericWebApplicationContext wac = new GenericWebApplicationContext();
1064-
wac.registerBeanDefinition("controller",
1065-
new RootBeanDefinition(ModelAndViewResolverController.class));
1095+
wac.registerBeanDefinition("controller", new RootBeanDefinition(ModelAndViewResolverController.class));
10661096
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
1067-
adapterDef.getPropertyValues()
1068-
.add("customModelAndViewResolver", new MyModelAndViewResolver());
1097+
adapterDef.getPropertyValues().add("customModelAndViewResolver", new MyModelAndViewResolver());
10691098
wac.registerBeanDefinition("handlerAdapter", adapterDef);
10701099
wac.refresh();
10711100
return wac;
@@ -1086,7 +1115,7 @@ public void bindingCookieValue() throws ServletException, IOException {
10861115

10871116
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test");
10881117
request.setCookies(new Cookie("date", "2008-11-18"));
1089-
MockHttpServletResponse response = new MockHttpServletResponse();
1118+
MockHttpServletResponse response = new MockHttpServletResponse();
10901119
servlet.service(request, response);
10911120
assertEquals("test-108", response.getContentAsString());
10921121
}
@@ -1096,7 +1125,7 @@ public void ambiguousParams() throws ServletException, IOException {
10961125
initServlet(AmbiguousParamsController.class);
10971126

10981127
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test");
1099-
MockHttpServletResponse response = new MockHttpServletResponse();
1128+
MockHttpServletResponse response = new MockHttpServletResponse();
11001129
servlet.service(request, response);
11011130
assertEquals("noParams", response.getContentAsString());
11021131

@@ -1116,7 +1145,6 @@ public void bridgeMethods() throws Exception {
11161145
servlet.service(request, response);
11171146
}
11181147

1119-
11201148
@Test
11211149
public void requestParamMap() throws Exception {
11221150
initServlet(RequestParamMapController.class);
@@ -1229,11 +1257,9 @@ protected WebApplicationContext createWebApplicationContext(WebApplicationContex
12291257
assertEquals("create", response.getContentAsString());
12301258
}
12311259

1232-
1233-
12341260
/*
1235-
* Controllers
1236-
*/
1261+
* Controllers
1262+
*/
12371263

12381264
@RequestMapping("/myPath.do")
12391265
private static class MyController extends AbstractController {
@@ -1470,7 +1496,8 @@ private static class MyCommandProvidingFormController<T, TB, TB2> extends MyForm
14701496
@SuppressWarnings("unused")
14711497
@ModelAttribute("myCommand")
14721498
private ValidTestBean createTestBean(@RequestParam T defaultName,
1473-
Map<String, Object> model, @RequestParam Date date) {
1499+
Map<String, Object> model,
1500+
@RequestParam Date date) {
14741501
model.put("myKey", "myOriginalValue");
14751502
ValidTestBean tb = new ValidTestBean();
14761503
tb.setName(defaultName.getClass().getSimpleName() + ":" + defaultName.toString());
@@ -1740,9 +1767,10 @@ public void render(Map model, HttpServletRequest request, HttpServletResponse re
17401767
}
17411768
List<TestBean> testBeans = (List<TestBean>) model.get("testBeanList");
17421769
if (errors.hasFieldErrors("age")) {
1743-
response.getWriter().write(viewName + "-" + tb.getName() + "-" +
1744-
errors.getFieldError("age").getCode() + "-" + testBeans.get(0).getName() + "-" +
1745-
model.get("myKey") + (model.containsKey("yourKey") ? "-" + model.get("yourKey") : ""));
1770+
response.getWriter()
1771+
.write(viewName + "-" + tb.getName() + "-" + errors.getFieldError("age").getCode() +
1772+
"-" + testBeans.get(0).getName() + "-" + model.get("myKey") +
1773+
(model.containsKey("yourKey") ? "-" + model.get("yourKey") : ""));
17461774
}
17471775
else {
17481776
response.getWriter().write(viewName + "-" + tb.getName() + "-" + tb.getAge() + "-" +
@@ -1760,6 +1788,7 @@ public View resolveViewName(String viewName, Locale locale) throws Exception {
17601788
public String getContentType() {
17611789
return null;
17621790
}
1791+
17631792
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
17641793
request.getSession().setAttribute("model", model);
17651794
}
@@ -1787,6 +1816,7 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp, @RequestPara
17871816
@Retention(RetentionPolicy.RUNTIME)
17881817
@Controller
17891818
public @interface MyControllerAnnotation {
1819+
17901820
}
17911821

17921822
@MyControllerAnnotation
@@ -1835,8 +1865,8 @@ public static class DefaultExpressionValueParamController {
18351865
@RequestMapping("/myPath.do")
18361866
public void myHandle(@RequestParam(value = "id", defaultValue = "${myKey}") String id,
18371867
@RequestHeader(defaultValue = "#{systemProperties.myHeader}") String header,
1838-
@Value("#{request.contextPath}") String contextPath, HttpServletResponse response)
1839-
throws IOException {
1868+
@Value("#{request.contextPath}") String contextPath,
1869+
HttpServletResponse response) throws IOException {
18401870
response.getWriter().write(String.valueOf(id) + "-" + String.valueOf(header) + "-" + contextPath);
18411871
}
18421872
}
@@ -1873,7 +1903,6 @@ public void get() {
18731903
}
18741904
}
18751905

1876-
18771906
@Controller
18781907
public static class PathOrderingController {
18791908

@@ -1888,7 +1917,6 @@ public void method2(Writer writer) throws IOException {
18881917
}
18891918
}
18901919

1891-
18921920
@Controller
18931921
public static class RequestBodyController {
18941922

@@ -1901,7 +1929,11 @@ public String handle(@RequestBody String body) throws IOException {
19011929

19021930
public static class MyMessageConverter implements HttpMessageConverter {
19031931

1904-
public boolean supports(Class clazz) {
1932+
public boolean canRead(Class clazz, MediaType mediaType) {
1933+
return true;
1934+
}
1935+
1936+
public boolean canWrite(Class clazz, MediaType mediaType) {
19051937
return true;
19061938
}
19071939

@@ -1914,7 +1946,7 @@ public Object read(Class clazz, HttpInputMessage inputMessage)
19141946
throw new HttpMessageNotReadableException("Could not read");
19151947
}
19161948

1917-
public void write(Object o, HttpOutputMessage outputMessage)
1949+
public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage)
19181950
throws IOException, HttpMessageNotWritableException {
19191951
throw new UnsupportedOperationException("Not implemented");
19201952
}
@@ -1955,8 +1987,11 @@ public MySpecialArg handle() {
19551987

19561988
public static class MyModelAndViewResolver implements ModelAndViewResolver {
19571989

1958-
public ModelAndView resolveModelAndView(Method handlerMethod, Class handlerType,
1959-
Object returnValue, ExtendedModelMap implicitModel, NativeWebRequest webRequest) {
1990+
public ModelAndView resolveModelAndView(Method handlerMethod,
1991+
Class handlerType,
1992+
Object returnValue,
1993+
ExtendedModelMap implicitModel,
1994+
NativeWebRequest webRequest) {
19601995
if (returnValue instanceof MySpecialArg) {
19611996
return new ModelAndView(new View() {
19621997
public String getContentType() {
@@ -2002,8 +2037,7 @@ public void initBinder(WebDataBinder binder) {
20022037
}
20032038

20042039
@RequestMapping(method = RequestMethod.GET)
2005-
public void handle(@CookieValue("date") Date date, Writer writer)
2006-
throws IOException {
2040+
public void handle(@CookieValue("date") Date date, Writer writer) throws IOException {
20072041
assertEquals("Invalid path variable value", new Date(108, 10, 18), date);
20082042
writer.write("test-" + date.getYear());
20092043
}
@@ -2043,7 +2077,8 @@ public void map(@RequestParam Map<String, String> params, Writer writer) throws
20432077
}
20442078

20452079
@RequestMapping("/multiValueMap")
2046-
public void multiValueMap(@RequestParam MultiValueMap<String, String> params, Writer writer) throws IOException {
2080+
public void multiValueMap(@RequestParam MultiValueMap<String, String> params, Writer writer)
2081+
throws IOException {
20472082
for (Iterator<Map.Entry<String, List<String>>> it1 = params.entrySet().iterator(); it1.hasNext();) {
20482083
Map.Entry<String, List<String>> entry = it1.next();
20492084
writer.write(entry.getKey() + "=[");
@@ -2078,7 +2113,8 @@ public void map(@RequestHeader Map<String, String> headers, Writer writer) throw
20782113
}
20792114

20802115
@RequestMapping("/multiValueMap")
2081-
public void multiValueMap(@RequestHeader MultiValueMap<String, String> headers, Writer writer) throws IOException {
2116+
public void multiValueMap(@RequestHeader MultiValueMap<String, String> headers, Writer writer)
2117+
throws IOException {
20822118
for (Iterator<Map.Entry<String, List<String>>> it1 = headers.entrySet().iterator(); it1.hasNext();) {
20832119
Map.Entry<String, List<String>> entry = it1.next();
20842120
writer.write(entry.getKey() + "=[");
@@ -2104,5 +2140,5 @@ public void httpHeaders(@RequestHeader HttpHeaders headers, Writer writer) throw
21042140

21052141
}
21062142

2107-
2143+
21082144
}

0 commit comments

Comments
 (0)