Peek into the content for @RequestBody(required=false)

For @RequestBody(required=false), it is not sufficient to check if the
InputStream is null. This change reads the first byte of the request
InputStream to determine if the request body is empty or not.

If the InputStream implementation supports mark(int) and reset(), then
we use those. Otherwise we use PushbackInputStream to read and unread
the first byte.

All of this is done only if the required flag of @RequestBody is set
to "false" (default is "true").

Issue: SPR-9942
master
Rossen Stoyanchev 12 years ago
parent 742d5f6f38
commit 0e3aa0eec4
  1. 46
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java

@ -17,16 +17,21 @@
package org.springframework.web.servlet.mvc.method.annotation; package org.springframework.web.servlet.mvc.method.annotation;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.Conventions; import org.springframework.core.Conventions;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors; import org.springframework.validation.Errors;
import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotAcceptableException;
@ -134,18 +139,45 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
} }
@Override @Override
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, protected <T> Object readWithMessageConverters(NativeWebRequest webRequest,
MethodParameter methodParam, Type paramType) throws IOException, HttpMediaTypeNotSupportedException { MethodParameter methodParam, Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
if (inputMessage.getBody() != null) { final HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
return super.readWithMessageConverters(inputMessage, methodParam, paramType); HttpInputMessage inputMessage = new ServletServerHttpRequest(servletRequest);
}
RequestBody annot = methodParam.getParameterAnnotation(RequestBody.class); RequestBody annot = methodParam.getParameterAnnotation(RequestBody.class);
if (!annot.required()) { if (!annot.required()) {
return null; InputStream inputStream = inputMessage.getBody();
if (inputStream == null) {
return null;
}
else if (inputStream.markSupported()) {
inputStream.mark(1);
if (inputStream.read() == -1) {
return null;
}
inputStream.reset();
}
else {
final PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
int b = pushbackInputStream.read();
if (b == -1) {
return null;
}
else {
pushbackInputStream.unread(b);
}
inputMessage = new ServletServerHttpRequest(servletRequest) {
@Override
public InputStream getBody() throws IOException {
// Form POST should not get here
return pushbackInputStream;
}
};
}
} }
throw new HttpMessageNotReadableException("Required request body content is missing: " + methodParam.toString());
return super.readWithMessageConverters(inputMessage, methodParam, paramType);
} }
public void handleReturnValue(Object returnValue, MethodParameter returnType, public void handleReturnValue(Object returnValue, MethodParameter returnType,

Loading…
Cancel
Save