SPR-5409 - Support for PUTting and POSTing non-form data
This commit is contained in:
parent
93c56f19df
commit
035eea01e8
|
|
@ -60,6 +60,7 @@ import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.core.style.StylerUtils;
|
import org.springframework.core.style.StylerUtils;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.ui.ExtendedModelMap;
|
import org.springframework.ui.ExtendedModelMap;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
@ -547,7 +548,7 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
||||||
|
|
||||||
public PortletHandlerMethodInvoker(HandlerMethodResolver resolver) {
|
public PortletHandlerMethodInvoker(HandlerMethodResolver resolver) {
|
||||||
super(resolver, webBindingInitializer, sessionAttributeStore,
|
super(resolver, webBindingInitializer, sessionAttributeStore,
|
||||||
parameterNameDiscoverer, customArgumentResolvers);
|
parameterNameDiscoverer, customArgumentResolvers, new HttpMessageConverter[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -19,9 +19,11 @@ package org.springframework.web.bind.annotation.support;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -35,6 +37,9 @@ import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
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.ExtendedModelMap;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
@ -42,11 +47,13 @@ import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.validation.Errors;
|
import org.springframework.validation.Errors;
|
||||||
|
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.CookieValue;
|
import org.springframework.web.bind.annotation.CookieValue;
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
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.RequestHeader;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||||
|
|
@ -89,24 +96,28 @@ public class HandlerMethodInvoker {
|
||||||
|
|
||||||
private final SimpleSessionStatus sessionStatus = new SimpleSessionStatus();
|
private final SimpleSessionStatus sessionStatus = new SimpleSessionStatus();
|
||||||
|
|
||||||
|
private final HttpMessageConverter[] messageConverters;
|
||||||
|
|
||||||
|
|
||||||
public HandlerMethodInvoker(HandlerMethodResolver methodResolver) {
|
public HandlerMethodInvoker(HandlerMethodResolver methodResolver) {
|
||||||
this(methodResolver, null);
|
this(methodResolver, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer) {
|
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,
|
public HandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer,
|
||||||
SessionAttributeStore sessionAttributeStore, ParameterNameDiscoverer parameterNameDiscoverer,
|
SessionAttributeStore sessionAttributeStore, ParameterNameDiscoverer parameterNameDiscoverer,
|
||||||
WebArgumentResolver... customArgumentResolvers) {
|
WebArgumentResolver[] customArgumentResolvers, HttpMessageConverter[] messageConverters) {
|
||||||
|
|
||||||
this.methodResolver = methodResolver;
|
this.methodResolver = methodResolver;
|
||||||
this.bindingInitializer = bindingInitializer;
|
this.bindingInitializer = bindingInitializer;
|
||||||
this.sessionAttributeStore = sessionAttributeStore;
|
this.sessionAttributeStore = sessionAttributeStore;
|
||||||
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||||
this.customArgumentResolvers = customArgumentResolvers;
|
this.customArgumentResolvers = customArgumentResolvers;
|
||||||
|
this.messageConverters = messageConverters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -159,6 +170,7 @@ public class HandlerMethodInvoker {
|
||||||
GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
|
GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
|
||||||
String paramName = null;
|
String paramName = null;
|
||||||
String headerName = null;
|
String headerName = null;
|
||||||
|
boolean requestBodyFound = false;
|
||||||
String cookieName = null;
|
String cookieName = null;
|
||||||
String pathVarName = null;
|
String pathVarName = null;
|
||||||
String attrName = null;
|
String attrName = null;
|
||||||
|
|
@ -182,6 +194,10 @@ public class HandlerMethodInvoker {
|
||||||
defaultValue = requestHeader.defaultValue();
|
defaultValue = requestHeader.defaultValue();
|
||||||
found++;
|
found++;
|
||||||
}
|
}
|
||||||
|
else if (RequestBody.class.isInstance(paramAnn)) {
|
||||||
|
requestBodyFound = true;
|
||||||
|
found++;
|
||||||
|
}
|
||||||
else if (CookieValue.class.isInstance(paramAnn)) {
|
else if (CookieValue.class.isInstance(paramAnn)) {
|
||||||
CookieValue cookieValue = (CookieValue) paramAnn;
|
CookieValue cookieValue = (CookieValue) paramAnn;
|
||||||
cookieName = cookieValue.value();
|
cookieName = cookieValue.value();
|
||||||
|
|
@ -238,6 +254,9 @@ public class HandlerMethodInvoker {
|
||||||
else if (headerName != null) {
|
else if (headerName != null) {
|
||||||
args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
|
args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
|
||||||
}
|
}
|
||||||
|
else if (requestBodyFound) {
|
||||||
|
args[i] = resolveRequestBody(methodParam, webRequest, handler);
|
||||||
|
}
|
||||||
else if (cookieName != null) {
|
else if (cookieName != null) {
|
||||||
args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
|
args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
|
||||||
}
|
}
|
||||||
|
|
@ -418,6 +437,45 @@ public class HandlerMethodInvoker {
|
||||||
return binder.convertIfNecessary(headerValue, paramType, methodParam);
|
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<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
|
||||||
|
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,
|
private Object resolveCookieValue(String cookieName, boolean required, String defaultValue,
|
||||||
MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
|
MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,14 @@ import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
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.ExtendedModelMap;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.util.AntPathMatcher;
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
|
@ -60,6 +68,7 @@ import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.PathMatcher;
|
import org.springframework.util.PathMatcher;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.validation.support.BindingAwareModelMap;
|
import org.springframework.validation.support.BindingAwareModelMap;
|
||||||
|
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||||
import org.springframework.web.HttpSessionRequiredException;
|
import org.springframework.web.HttpSessionRequiredException;
|
||||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||||
|
|
@ -149,6 +158,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
private final Map<Class<?>, ServletHandlerMethodResolver> methodResolverCache =
|
private final Map<Class<?>, ServletHandlerMethodResolver> methodResolverCache =
|
||||||
new ConcurrentHashMap<Class<?>, ServletHandlerMethodResolver>();
|
new ConcurrentHashMap<Class<?>, ServletHandlerMethodResolver>();
|
||||||
|
|
||||||
|
private HttpMessageConverter<?>[] messageConverters =
|
||||||
|
new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter(),
|
||||||
|
new FormHttpMessageConverter(), new SourceHttpMessageConverter()};
|
||||||
|
|
||||||
public AnnotationMethodHandlerAdapter() {
|
public AnnotationMethodHandlerAdapter() {
|
||||||
// no restriction of HTTP methods by default
|
// no restriction of HTTP methods by default
|
||||||
|
|
@ -291,6 +303,16 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
this.customArgumentResolvers = argumentResolvers;
|
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) {
|
public boolean supports(Object handler) {
|
||||||
return getMethodResolver(handler).hasHandlerMethods();
|
return getMethodResolver(handler).hasHandlerMethods();
|
||||||
|
|
@ -346,13 +368,15 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
catch (HttpRequestMethodNotSupportedException ex) {
|
catch (HttpRequestMethodNotSupportedException ex) {
|
||||||
return handleHttpRequestMethodNotSupportedException(ex, request, response);
|
return handleHttpRequestMethodNotSupportedException(ex, request, response);
|
||||||
}
|
}
|
||||||
|
catch (HttpMediaTypeNotSupportedException ex) {
|
||||||
|
return handleHttpMediaTypeNotSupportedException(ex, request, response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getLastModified(HttpServletRequest request, Object handler) {
|
public long getLastModified(HttpServletRequest request, Object handler) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the case where no request handler method was found.
|
* Handle the case where no request handler method was found.
|
||||||
* <p>The default implementation logs a warning and sends an HTTP 404 error.
|
* <p>The default implementation logs a warning and sends an HTTP 404 error.
|
||||||
|
|
@ -394,6 +418,27 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the case where no {@linkplain HttpMessageConverter message converters} was found for the PUT or POSTed
|
||||||
|
* content.
|
||||||
|
* <p>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 <code>null</code> 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.
|
* Template method for creating a new ServletRequestDataBinder instance.
|
||||||
* <p>The default implementation creates a standard ServletRequestDataBinder.
|
* <p>The default implementation creates a standard ServletRequestDataBinder.
|
||||||
|
|
@ -593,7 +638,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
|
|
||||||
private ServletHandlerMethodInvoker(HandlerMethodResolver resolver) {
|
private ServletHandlerMethodInvoker(HandlerMethodResolver resolver) {
|
||||||
super(resolver, webBindingInitializer, sessionAttributeStore,
|
super(resolver, webBindingInitializer, sessionAttributeStore,
|
||||||
parameterNameDiscoverer, customArgumentResolvers);
|
parameterNameDiscoverer, customArgumentResolvers, messageConverters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
@Override
|
||||||
protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
|
protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import org.junit.Test;
|
||||||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||||
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
|
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
|
||||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.DerivedTestBean;
|
import org.springframework.beans.DerivedTestBean;
|
||||||
import org.springframework.beans.ITestBean;
|
import org.springframework.beans.ITestBean;
|
||||||
import org.springframework.beans.TestBean;
|
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.CookieValue;
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
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.RequestHeader;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
|
@ -93,18 +95,11 @@ import org.springframework.web.util.NestedServletException;
|
||||||
*/
|
*/
|
||||||
public class ServletAnnotationControllerTests {
|
public class ServletAnnotationControllerTests {
|
||||||
|
|
||||||
|
private DispatcherServlet servlet;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void standardHandleMethod() throws Exception {
|
public void standardHandleMethod() throws Exception {
|
||||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
initServlet(MyController.class);
|
||||||
@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());
|
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
@ -114,16 +109,7 @@ public class ServletAnnotationControllerTests {
|
||||||
|
|
||||||
@Test(expected = MissingServletRequestParameterException.class)
|
@Test(expected = MissingServletRequestParameterException.class)
|
||||||
public void requiredParamMissing() throws Exception {
|
public void requiredParamMissing() throws Exception {
|
||||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
initServlet(RequiredParamController.class);
|
||||||
@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());
|
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
@ -132,16 +118,7 @@ public class ServletAnnotationControllerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void optionalParamPresent() throws Exception {
|
public void optionalParamPresent() throws Exception {
|
||||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
initServlet(OptionalParamController.class);
|
||||||
@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());
|
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
||||||
request.addParameter("id", "val");
|
request.addParameter("id", "val");
|
||||||
|
|
@ -154,16 +131,7 @@ public class ServletAnnotationControllerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void optionalParamMissing() throws Exception {
|
public void optionalParamMissing() throws Exception {
|
||||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
initServlet(OptionalParamController.class);
|
||||||
@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());
|
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
@ -173,16 +141,7 @@ public class ServletAnnotationControllerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void defaultParamMissing() throws Exception {
|
public void defaultParamMissing() throws Exception {
|
||||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
initServlet(DefaultValueParamController.class);
|
||||||
@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());
|
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
@ -192,16 +151,7 @@ public class ServletAnnotationControllerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void methodNotAllowed() throws Exception {
|
public void methodNotAllowed() throws Exception {
|
||||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
initServlet(MethodNotAllowedController.class);
|
||||||
@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());
|
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
@ -285,17 +235,23 @@ public class ServletAnnotationControllerTests {
|
||||||
doTestAdaptedHandleMethods(MyAdaptedController3.class);
|
doTestAdaptedHandleMethods(MyAdaptedController3.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doTestAdaptedHandleMethods(final Class<?> controllerClass) throws Exception {
|
private void initServlet(final Class<?> controllerclass) throws ServletException {
|
||||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
servlet = new DispatcherServlet() {
|
||||||
@Override
|
@Override
|
||||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)
|
||||||
|
throws BeansException {
|
||||||
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||||
wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerClass));
|
wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerclass));
|
||||||
wac.refresh();
|
wac.refresh();
|
||||||
return wac;
|
return wac;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
servlet.init(new MockServletConfig());
|
servlet.init(new MockServletConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void doTestAdaptedHandleMethods(final Class<?> controllerClass) throws Exception {
|
||||||
|
initServlet(controllerClass);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath1.do");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath1.do");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
@ -886,16 +842,7 @@ public class ServletAnnotationControllerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pathOrdering() throws ServletException, IOException {
|
public void pathOrdering() throws ServletException, IOException {
|
||||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
initServlet(PathOrderingController.class);
|
||||||
@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());
|
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/dir/myPath1.do");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/dir/myPath1.do");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
@ -903,6 +850,34 @@ public class ServletAnnotationControllerTests {
|
||||||
assertEquals("method1", response.getContentAsString());
|
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
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<MediaType> supportedMediaTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new HttpMediaTypeNotSupportedException.
|
||||||
|
* @param contentType the unsupported content type
|
||||||
|
* @param supportedMediaTypes the list of supported media types
|
||||||
|
*/
|
||||||
|
public HttpMediaTypeNotSupportedException(MediaType contentType, List<MediaType> 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<MediaType> 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<MediaType> getSupportedMediaTypes() {
|
||||||
|
return supportedMediaTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue