diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index c121470875d..e103b1bb1e0 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java @@ -60,6 +60,7 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.style.StylerUtils; +import org.springframework.http.converter.HttpMessageConverter; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.util.Assert; @@ -547,7 +548,7 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl public PortletHandlerMethodInvoker(HandlerMethodResolver resolver) { super(resolver, webBindingInitializer, sessionAttributeStore, - parameterNameDiscoverer, customArgumentResolvers); + parameterNameDiscoverer, customArgumentResolvers, new HttpMessageConverter[0]); } @Override diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestBody.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestBody.java new file mode 100644 index 00000000000..540540fa8c7 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestBody.java @@ -0,0 +1,23 @@ +package org.springframework.web.bind.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation which indicates that a method parameter should be bound to the web request body. Supported for annotated + * handler methods in Servlet environments. + * + * @author Arjen Poutsma + * @see RequestHeader + * @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter + * @since 3.0 + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RequestBody { + +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java index 3fb397098fc..9ba572d88fe 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java @@ -19,9 +19,11 @@ package org.springframework.web.bind.annotation.support; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -35,6 +37,9 @@ import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.util.ClassUtils; @@ -42,11 +47,13 @@ import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; +import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.support.DefaultSessionAttributeStore; @@ -89,24 +96,28 @@ public class HandlerMethodInvoker { private final SimpleSessionStatus sessionStatus = new SimpleSessionStatus(); + private final HttpMessageConverter[] messageConverters; + public HandlerMethodInvoker(HandlerMethodResolver methodResolver) { this(methodResolver, null); } public HandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer) { - this(methodResolver, bindingInitializer, new DefaultSessionAttributeStore(), null); + this(methodResolver, bindingInitializer, new DefaultSessionAttributeStore(), null, new WebArgumentResolver[0], + new HttpMessageConverter[0]); } public HandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer, SessionAttributeStore sessionAttributeStore, ParameterNameDiscoverer parameterNameDiscoverer, - WebArgumentResolver... customArgumentResolvers) { + WebArgumentResolver[] customArgumentResolvers, HttpMessageConverter[] messageConverters) { this.methodResolver = methodResolver; this.bindingInitializer = bindingInitializer; this.sessionAttributeStore = sessionAttributeStore; this.parameterNameDiscoverer = parameterNameDiscoverer; this.customArgumentResolvers = customArgumentResolvers; + this.messageConverters = messageConverters; } @@ -159,6 +170,7 @@ public class HandlerMethodInvoker { GenericTypeResolver.resolveParameterType(methodParam, handler.getClass()); String paramName = null; String headerName = null; + boolean requestBodyFound = false; String cookieName = null; String pathVarName = null; String attrName = null; @@ -182,6 +194,10 @@ public class HandlerMethodInvoker { defaultValue = requestHeader.defaultValue(); found++; } + else if (RequestBody.class.isInstance(paramAnn)) { + requestBodyFound = true; + found++; + } else if (CookieValue.class.isInstance(paramAnn)) { CookieValue cookieValue = (CookieValue) paramAnn; cookieName = cookieValue.value(); @@ -238,6 +254,9 @@ public class HandlerMethodInvoker { else if (headerName != null) { args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler); } + else if (requestBodyFound) { + args[i] = resolveRequestBody(methodParam, webRequest, handler); + } else if (cookieName != null) { args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler); } @@ -418,6 +437,45 @@ public class HandlerMethodInvoker { return binder.convertIfNecessary(headerValue, paramType, methodParam); } + /** + * Resolves the given {@link RequestBody @RequestBody} annotation. + * Throws an UnsupportedOperationException by default. + */ + @SuppressWarnings("unchecked") + protected Object resolveRequestBody(MethodParameter methodParam, NativeWebRequest webRequest, Object handler) + throws Exception { + + HttpInputMessage inputMessage = createHttpInputMessage(webRequest); + + Class paramType = methodParam.getParameterType(); + MediaType contentType = inputMessage.getHeaders().getContentType(); + if (contentType == null) { + throw new IllegalStateException("Cannot extract response: no Content-Type found"); + } + List allSupportedMediaTypes = new ArrayList(); + for (HttpMessageConverter messageConverter : messageConverters) { + allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); + if (messageConverter.supports(paramType)) { + for (MediaType supportedMediaType : messageConverter.getSupportedMediaTypes()) { + if (supportedMediaType.includes(contentType)) { + return messageConverter.read(paramType, inputMessage); + } + } + } + } + + throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes); + } + + /** + * Returns a {@link HttpInputMessage} for the given {@link NativeWebRequest}. + * Throws an UnsupportedOperationException by default. + */ + protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception { + + throw new UnsupportedOperationException("@RequestBody not supported"); + } + private Object resolveCookieValue(String cookieName, boolean required, String defaultValue, MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall) throws Exception { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index 8546c1860b2..3b419798e3e 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java @@ -51,6 +51,14 @@ import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.ByteArrayHttpMessageConverter; +import org.springframework.http.converter.FormHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.xml.SourceHttpMessageConverter; +import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.util.AntPathMatcher; @@ -60,6 +68,7 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.PathMatcher; import org.springframework.util.StringUtils; import org.springframework.validation.support.BindingAwareModelMap; +import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpSessionRequiredException; import org.springframework.web.bind.MissingServletRequestParameterException; @@ -149,6 +158,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen private final Map, ServletHandlerMethodResolver> methodResolverCache = new ConcurrentHashMap, ServletHandlerMethodResolver>(); + private HttpMessageConverter[] messageConverters = + new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter(), + new FormHttpMessageConverter(), new SourceHttpMessageConverter()}; public AnnotationMethodHandlerAdapter() { // no restriction of HTTP methods by default @@ -291,6 +303,16 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen this.customArgumentResolvers = argumentResolvers; } + /** + * Set the message body converters to use. These converters are used to convert + * from and to HTTP requests and responses. + */ + public void setMessageConverters(HttpMessageConverter[] messageConverters) { + Assert.notEmpty(messageConverters, "'messageConverters' must not be empty"); + this.messageConverters = messageConverters; + } + + public boolean supports(Object handler) { return getMethodResolver(handler).hasHandlerMethods(); @@ -346,13 +368,15 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen catch (HttpRequestMethodNotSupportedException ex) { return handleHttpRequestMethodNotSupportedException(ex, request, response); } + catch (HttpMediaTypeNotSupportedException ex) { + return handleHttpMediaTypeNotSupportedException(ex, request, response); + } } public long getLastModified(HttpServletRequest request, Object handler) { return -1; } - /** * Handle the case where no request handler method was found. *

The default implementation logs a warning and sends an HTTP 404 error. @@ -394,6 +418,27 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen return null; } + /** + * Handle the case where no {@linkplain HttpMessageConverter message converters} was found for the PUT or POSTed + * content. + *

The default implementation logs a warning, sends an HTTP 415 error and sets the "Allow" header. + * Alternatively, a fallback view could be chosen, or the HttpMediaTypeNotSupportedException + * could be rethrown as-is. + * @param ex the HttpMediaTypeNotSupportedException to be handled + * @param request current HTTP request + * @param response current HTTP response + * @return a ModelAndView to render, or null if handled directly + * @throws Exception an Exception that should be thrown as result of the servlet request + */ + protected ModelAndView handleHttpMediaTypeNotSupportedException( + HttpMediaTypeNotSupportedException ex, HttpServletRequest request, HttpServletResponse response) + throws Exception { + + response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + response.addHeader("Accept", MediaType.toString(ex.getSupportedMediaTypes())); + return null; + } + /** * Template method for creating a new ServletRequestDataBinder instance. *

The default implementation creates a standard ServletRequestDataBinder. @@ -593,7 +638,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen private ServletHandlerMethodInvoker(HandlerMethodResolver resolver) { super(resolver, webBindingInitializer, sessionAttributeStore, - parameterNameDiscoverer, customArgumentResolvers); + parameterNameDiscoverer, customArgumentResolvers, messageConverters); } @Override @@ -625,6 +670,12 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } } + @Override + protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception { + HttpServletRequest servletRequest = (HttpServletRequest) webRequest.getNativeRequest(); + return new ServletServerHttpRequest(servletRequest); + } + @Override protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest) throws Exception { diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java index 0740744aef2..74efeaf7341 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java @@ -42,6 +42,7 @@ import org.junit.Test; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.aop.interceptor.SimpleTraceInterceptor; import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.beans.BeansException; import org.springframework.beans.DerivedTestBean; import org.springframework.beans.ITestBean; import org.springframework.beans.TestBean; @@ -66,6 +67,7 @@ import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -93,18 +95,11 @@ import org.springframework.web.util.NestedServletException; */ public class ServletAnnotationControllerTests { + private DispatcherServlet servlet; + @Test public void standardHandleMethod() throws Exception { - @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { - @Override - protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { - GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(MyController.class)); - wac.refresh(); - return wac; - } - }; - servlet.init(new MockServletConfig()); + initServlet(MyController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -114,16 +109,7 @@ public class ServletAnnotationControllerTests { @Test(expected = MissingServletRequestParameterException.class) public void requiredParamMissing() throws Exception { - @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { - @Override - protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { - GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(RequiredParamController.class)); - wac.refresh(); - return wac; - } - }; - servlet.init(new MockServletConfig()); + initServlet(RequiredParamController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -132,16 +118,7 @@ public class ServletAnnotationControllerTests { @Test public void optionalParamPresent() throws Exception { - @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { - @Override - protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { - GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(OptionalParamController.class)); - wac.refresh(); - return wac; - } - }; - servlet.init(new MockServletConfig()); + initServlet(OptionalParamController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); request.addParameter("id", "val"); @@ -154,16 +131,7 @@ public class ServletAnnotationControllerTests { @Test public void optionalParamMissing() throws Exception { - @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { - @Override - protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { - GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(OptionalParamController.class)); - wac.refresh(); - return wac; - } - }; - servlet.init(new MockServletConfig()); + initServlet(OptionalParamController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -173,16 +141,7 @@ public class ServletAnnotationControllerTests { @Test public void defaultParamMissing() throws Exception { - @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { - @Override - protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { - GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(DefaultValueParamController.class)); - wac.refresh(); - return wac; - } - }; - servlet.init(new MockServletConfig()); + initServlet(DefaultValueParamController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -192,16 +151,7 @@ public class ServletAnnotationControllerTests { @Test public void methodNotAllowed() throws Exception { - @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { - @Override - protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { - GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(MethodNotAllowedController.class)); - wac.refresh(); - return wac; - } - }; - servlet.init(new MockServletConfig()); + initServlet(MethodNotAllowedController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -285,17 +235,23 @@ public class ServletAnnotationControllerTests { doTestAdaptedHandleMethods(MyAdaptedController3.class); } - private void doTestAdaptedHandleMethods(final Class controllerClass) throws Exception { - @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { + private void initServlet(final Class controllerclass) throws ServletException { + servlet = new DispatcherServlet() { @Override - protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { + protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) + throws BeansException { GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerClass)); + wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerclass)); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); + } + + + private void doTestAdaptedHandleMethods(final Class controllerClass) throws Exception { + initServlet(controllerClass); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath1.do"); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -886,16 +842,7 @@ public class ServletAnnotationControllerTests { @Test public void pathOrdering() throws ServletException, IOException { - @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { - @Override - protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { - GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(PathOrderingController.class)); - wac.refresh(); - return wac; - } - }; - servlet.init(new MockServletConfig()); + initServlet(PathOrderingController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/dir/myPath1.do"); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -903,6 +850,34 @@ public class ServletAnnotationControllerTests { assertEquals("method1", response.getContentAsString()); } + @Test + public void requestBody() 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"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(requestBody, response.getContentAsString()); + } + + @Test + public void unsupportedRequestBody() 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", "application/pdf"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Invalid response status code", HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, + response.getStatus()); + assertNotNull("No Accept response header set", response.getHeader("Accept")); + } + /* * Controllers */ @@ -1482,4 +1457,13 @@ public class ServletAnnotationControllerTests { } } + @Controller + public static class RequestBodyController { + + @RequestMapping(value = "/something", method = RequestMethod.PUT) + public void handle(@RequestBody String body, Writer writer) throws IOException { + writer.write(body); + } + } + } diff --git a/org.springframework.web/src/main/java/org/springframework/web/HttpMediaTypeNotSupportedException.java b/org.springframework.web/src/main/java/org/springframework/web/HttpMediaTypeNotSupportedException.java new file mode 100644 index 00000000000..ae9d9fd2cf0 --- /dev/null +++ b/org.springframework.web/src/main/java/org/springframework/web/HttpMediaTypeNotSupportedException.java @@ -0,0 +1,56 @@ +package org.springframework.web; + +import java.util.List; +import javax.servlet.ServletException; + +import org.springframework.http.MediaType; + +/** + * Exception thrown when a client POSTs or PUTs content + * not supported by request handler does not support a + * specific request method. + * + * @author Arjen Poutsma + * @since 3.0 + */ +public class HttpMediaTypeNotSupportedException extends ServletException { + + private MediaType contentType; + + private List supportedMediaTypes; + + /** + * Create a new HttpMediaTypeNotSupportedException. + * @param contentType the unsupported content type + * @param supportedMediaTypes the list of supported media types + */ + public HttpMediaTypeNotSupportedException(MediaType contentType, List supportedMediaTypes) { + this(contentType, supportedMediaTypes, "Content type '" + contentType + "' not supported"); + } + + /** + * Create a new HttpMediaTypeNotSupportedException. + * @param contentType the unsupported content type + * @param supportedMediaTypes the list of supported media types + * @param msg the detail message + */ + public HttpMediaTypeNotSupportedException(MediaType contentType, List supportedMediaTypes, String msg) { + super(msg); + this.contentType = contentType; + this.supportedMediaTypes = supportedMediaTypes; + } + + /** + * Return the HTTP request content type method that caused the failure. + */ + public MediaType getContentType() { + return contentType; + } + + /** + * Return the list of supported media types. + */ + public List getSupportedMediaTypes() { + return supportedMediaTypes; + } +}