SPR-5923 - HttpMessageConverter selection as a result of @ResponseBody should consider the requested content type

This commit is contained in:
Arjen Poutsma
2009-07-28 13:12:12 +00:00
parent de234e6839
commit b11970ed8d
6 changed files with 182 additions and 24 deletions

View File

@@ -20,6 +20,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.ArrayList;
@@ -32,6 +33,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
@@ -51,6 +53,7 @@ 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.MediaType;
import org.springframework.http.converter.BufferedImageHttpMessageConverter;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.FormHttpMessageConverter;
@@ -71,6 +74,7 @@ import org.springframework.util.StringUtils;
import org.springframework.validation.support.BindingAwareModelMap;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.HttpSessionRequiredException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.WebDataBinder;
@@ -726,15 +730,7 @@ 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;
}
}
handleRequestBody(returnValue, webRequest);
}
if (returnValue instanceof ModelAndView) {
@@ -777,6 +773,31 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
}
}
@SuppressWarnings("unchecked")
private void handleRequestBody(Object returnValue, ServletWebRequest webRequest) throws ServletException, IOException {
HttpInputMessage inputMessage = new ServletServerHttpRequest(webRequest.getRequest());
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
HttpOutputMessage outputMessage = new ServletServerHttpResponse(webRequest.getResponse());
Class<?> returnValueType = returnValue.getClass();
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
for (HttpMessageConverter messageConverter : messageConverters) {
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
if (messageConverter.supports(returnValueType)) {
for (Object o : messageConverter.getSupportedMediaTypes()) {
MediaType supportedMediaType = (MediaType) o;
for (MediaType acceptedMediaType : acceptedMediaTypes) {
if (supportedMediaType.includes(acceptedMediaType)) {
messageConverter.write(returnValue, outputMessage);
responseArgumentUsed = true;
return;
}
}
}
}
}
throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
}
}
static class RequestMappingInfo {

View File

@@ -33,6 +33,7 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
@@ -93,6 +94,10 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,
handler);
}
else if (ex instanceof HttpMediaTypeNotAcceptableException) {
return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response,
handler);
}
else if (ex instanceof MissingServletRequestParameterException) {
return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request,
response, handler);
@@ -169,7 +174,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
/**
* Handle the case where no {@linkplain org.springframework.http.converter.HttpMessageConverter message converters}
* were found for the PUT or POSTed content. <p>The default implementation sends an HTTP 415 error, sets the "Allow"
* were found for the PUT or POSTed content. <p>The default implementation sends an HTTP 415 error, sets the "Accept"
* header, and returns an empty {@code ModelAndView}. Alternatively, a fallback view could be chosen, or the
* HttpMediaTypeNotSupportedException could be rethrown as-is.
*
@@ -194,6 +199,29 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
return new ModelAndView();
}
/**
* Handle the case where no {@linkplain org.springframework.http.converter.HttpMessageConverter message converters}
* were found that were acceptable for the client (expressed via the {@code Accept} header.
* <p>The default implementation sends an HTTP 406 error and returns an empty {@code ModelAndView}. Alternatively,
* a fallback view could be chosen, or the HttpMediaTypeNotAcceptableException could be rethrown as-is.
*
* @param ex the HttpMediaTypeNotAcceptableException to be handled
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
* if multipart resolution failed)
* @return a ModelAndView to render, or <code>null</code> if handled directly
* @throws Exception an Exception that should be thrown as result of the servlet request
*/
protected ModelAndView handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
return new ModelAndView();
}
/**
* Handle the case when a required parameter is missing. <p>The default implementation sends an HTTP 400 error, and
* returns an empty {@code ModelAndView}. Alternatively, a fallback view could be chosen, or the