提交 b11970ed 编写于 作者: A Arjen Poutsma

SPR-5923 - HttpMessageConverter selection as a result of @ResponseBody should...

SPR-5923 - HttpMessageConverter selection as a result of @ResponseBody should consider the requested content type
上级 de234e68
......@@ -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 {
......
......@@ -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
......
......@@ -876,11 +876,26 @@ public class ServletAnnotationControllerTests {
String requestBody = "Hello World";
request.setContent(requestBody.getBytes("UTF-8"));
request.addHeader("Content-Type", "text/plain; charset=utf-8");
request.addHeader("Accept", "text/*");
MockHttpServletResponse response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals(requestBody, response.getContentAsString());
}
@Test
public void responseBodyNoAcceptableMediaType() throws ServletException, IOException {
initServlet(RequestBodyController.class);
MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
String requestBody = "Hello World";
request.setContent(requestBody.getBytes("UTF-8"));
request.addHeader("Content-Type", "text/plain; charset=utf-8");
request.addHeader("Accept", "application/pdf, application/msword");
MockHttpServletResponse response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals(406, response.getStatus());
}
@Test
public void unsupportedRequestBody() throws ServletException, IOException {
initServlet(RequestBodyController.class);
......
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web;
import java.util.List;
import java.util.Collections;
import javax.servlet.ServletException;
import org.springframework.http.MediaType;
/**
* Abstract base for exceptions related to media types. Adds a list of supported {@link MediaType MediaTypes}.
*
* @author Arjen Poutsma
* @since 3.0
*/
public abstract class HttpMediaTypeException extends ServletException {
private final List<MediaType> supportedMediaTypes;
/**
* Create a new MediaTypeException.
* @param message the exception message
*/
protected HttpMediaTypeException(String message) {
super(message);
this.supportedMediaTypes = Collections.emptyList();
}
/**
* Create a new HttpMediaTypeNotSupportedException.
* @param supportedMediaTypes the list of supported media types
*/
protected HttpMediaTypeException(String message, List<MediaType> supportedMediaTypes) {
super(message);
this.supportedMediaTypes = supportedMediaTypes;
}
/**
* Return the list of supported media types.
*/
public List<MediaType> getSupportedMediaTypes() {
return supportedMediaTypes;
}
}
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web;
import java.util.List;
import org.springframework.http.MediaType;
/**
* Exception thrown when the request handler cannot generate a response that is acceptable by the client.
*
* @author Arjen Poutsma
* @since 3.0
*/
public class HttpMediaTypeNotAcceptableException extends HttpMediaTypeException {
/**
* Create a new HttpMediaTypeNotAcceptableException.
* @param message the exception message
*/
public HttpMediaTypeNotAcceptableException(String message) {
super(message);
}
/**
* Create a new HttpMediaTypeNotSupportedException.
* @param supportedMediaTypes the list of supported media types
*/
public HttpMediaTypeNotAcceptableException(List<MediaType> supportedMediaTypes) {
super("Could not find acceptable representation", supportedMediaTypes);
}
}
......@@ -16,9 +16,7 @@
package org.springframework.web;
import java.util.Collections;
import java.util.List;
import javax.servlet.ServletException;
import org.springframework.http.MediaType;
......@@ -29,12 +27,10 @@ import org.springframework.http.MediaType;
* @author Arjen Poutsma
* @since 3.0
*/
public class HttpMediaTypeNotSupportedException extends ServletException {
public class HttpMediaTypeNotSupportedException extends HttpMediaTypeException {
private final MediaType contentType;
private final List<MediaType> supportedMediaTypes;
/**
* Create a new HttpMediaTypeNotSupportedException.
* @param message the exception message
......@@ -42,7 +38,6 @@ public class HttpMediaTypeNotSupportedException extends ServletException {
public HttpMediaTypeNotSupportedException(String message) {
super(message);
this.contentType = null;
this.supportedMediaTypes = Collections.emptyList();
}
/**
......@@ -61,9 +56,8 @@ public class HttpMediaTypeNotSupportedException extends ServletException {
* @param msg the detail message
*/
public HttpMediaTypeNotSupportedException(MediaType contentType, List<MediaType> supportedMediaTypes, String msg) {
super(msg);
super(msg, supportedMediaTypes);
this.contentType = contentType;
this.supportedMediaTypes = supportedMediaTypes;
}
/**
......@@ -73,10 +67,4 @@ public class HttpMediaTypeNotSupportedException extends ServletException {
return contentType;
}
/**
* Return the list of supported media types.
*/
public List<MediaType> getSupportedMediaTypes() {
return supportedMediaTypes;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册