1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.servlet.mvc.method.annotation;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.PushbackInputStream;
22 import java.lang.reflect.Type;
23 import java.util.List;
24 import javax.servlet.http.HttpServletRequest;
25
26 import org.springframework.core.Conventions;
27 import org.springframework.core.MethodParameter;
28 import org.springframework.core.annotation.AnnotationUtils;
29 import org.springframework.http.HttpInputMessage;
30 import org.springframework.http.converter.HttpMessageConverter;
31 import org.springframework.http.converter.HttpMessageNotReadableException;
32 import org.springframework.http.server.ServletServerHttpRequest;
33 import org.springframework.validation.BindingResult;
34 import org.springframework.web.HttpMediaTypeNotAcceptableException;
35 import org.springframework.web.HttpMediaTypeNotSupportedException;
36 import org.springframework.web.accept.ContentNegotiationManager;
37 import org.springframework.web.bind.MethodArgumentNotValidException;
38 import org.springframework.web.bind.WebDataBinder;
39 import org.springframework.web.bind.annotation.RequestBody;
40 import org.springframework.web.bind.annotation.ResponseBody;
41 import org.springframework.web.bind.support.WebDataBinderFactory;
42 import org.springframework.web.context.request.NativeWebRequest;
43 import org.springframework.web.method.support.ModelAndViewContainer;
44 import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
61
62 public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> messageConverters) {
63 super(messageConverters);
64 }
65
66 public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
67 ContentNegotiationManager contentNegotiationManager) {
68
69 super(messageConverters, contentNegotiationManager);
70 }
71
72 public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> messageConverters,
73 ContentNegotiationManager contentNegotiationManager, List<Object> responseBodyAdvice) {
74
75 super(messageConverters, contentNegotiationManager, responseBodyAdvice);
76 }
77
78
79 @Override
80 public boolean supportsParameter(MethodParameter parameter) {
81 return parameter.hasParameterAnnotation(RequestBody.class);
82 }
83
84 @Override
85 public boolean supportsReturnType(MethodParameter returnType) {
86 return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null ||
87 returnType.getMethodAnnotation(ResponseBody.class) != null);
88 }
89
90
91
92
93
94
95
96 @Override
97 public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
98 NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
99
100 Object arg = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
101 String name = Conventions.getVariableNameForParameter(parameter);
102 WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
103 if (arg != null) {
104 validateIfApplicable(binder, parameter);
105 if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
106 throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
107 }
108 }
109 mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
110 return arg;
111 }
112
113 @Override
114 protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter methodParam,
115 Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
116
117 HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
118 HttpInputMessage inputMessage = new ServletServerHttpRequest(servletRequest);
119
120 InputStream inputStream = inputMessage.getBody();
121 if (inputStream == null) {
122 return handleEmptyBody(methodParam);
123 }
124 else if (inputStream.markSupported()) {
125 inputStream.mark(1);
126 if (inputStream.read() == -1) {
127 return handleEmptyBody(methodParam);
128 }
129 inputStream.reset();
130 }
131 else {
132 final PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
133 int b = pushbackInputStream.read();
134 if (b == -1) {
135 return handleEmptyBody(methodParam);
136 }
137 else {
138 pushbackInputStream.unread(b);
139 }
140 inputMessage = new ServletServerHttpRequest(servletRequest) {
141 @Override
142 public InputStream getBody() {
143
144 return pushbackInputStream;
145 }
146 };
147 }
148
149 return super.readWithMessageConverters(inputMessage, methodParam, paramType);
150 }
151
152 private Object handleEmptyBody(MethodParameter param) {
153 if (param.getParameterAnnotation(RequestBody.class).required()) {
154 throw new HttpMessageNotReadableException("Required request body content is missing: " + param);
155 }
156 return null;
157 }
158
159 @Override
160 public void handleReturnValue(Object returnValue, MethodParameter returnType,
161 ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
162 throws IOException, HttpMediaTypeNotAcceptableException {
163
164 mavContainer.setRequestHandled(true);
165
166
167 writeWithMessageConverters(returnValue, returnType, webRequest);
168 }
169
170 }