From 0e3aa0eec4b604957d5206257396616a93d1c7c3 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 31 Oct 2012 18:18:17 -0400 Subject: [PATCH] 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 --- .../RequestResponseBodyMethodProcessor.java | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java index c6569b77fa..9b320cedb9 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java +++ b/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; import java.io.IOException; +import java.io.InputStream; +import java.io.PushbackInputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.List; +import javax.servlet.http.HttpServletRequest; + import org.springframework.core.Conventions; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.HttpInputMessage; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.web.HttpMediaTypeNotAcceptableException; @@ -134,18 +139,45 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter } @Override - protected Object readWithMessageConverters(HttpInputMessage inputMessage, - MethodParameter methodParam, Type paramType) throws IOException, HttpMediaTypeNotSupportedException { + protected Object readWithMessageConverters(NativeWebRequest webRequest, + MethodParameter methodParam, Type paramType) throws IOException, HttpMediaTypeNotSupportedException { - if (inputMessage.getBody() != null) { - return super.readWithMessageConverters(inputMessage, methodParam, paramType); - } + final HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); + HttpInputMessage inputMessage = new ServletServerHttpRequest(servletRequest); RequestBody annot = methodParam.getParameterAnnotation(RequestBody.class); 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,