HttpMessageConverter.supports() is split into canRead/canWrite.
HttpMessageConverter.write() now allows for a specific content type.
This commit is contained in:
parent
18c63f70c4
commit
dc0613f487
|
|
@ -121,22 +121,24 @@ import org.springframework.web.util.WebUtils;
|
|||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @since 2.5
|
||||
* @see #setPathMatcher
|
||||
* @see #setMethodNameResolver
|
||||
* @see #setWebBindingInitializer
|
||||
* @see #setSessionAttributeStore
|
||||
* @since 2.5
|
||||
*/
|
||||
public class AnnotationMethodHandlerAdapter extends WebContentGenerator implements HandlerAdapter, BeanFactoryAware {
|
||||
|
||||
/**
|
||||
* Log category to use when no mapped handler is found for a request.
|
||||
*
|
||||
* @see #pageNotFoundLogger
|
||||
*/
|
||||
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
|
||||
|
||||
/**
|
||||
* Additional logger to use when no mapped handler is found for a request.
|
||||
*
|
||||
* @see #PAGE_NOT_FOUND_LOG_CATEGORY
|
||||
*/
|
||||
protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
|
||||
|
|
@ -162,7 +164,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
private ModelAndViewResolver[] customModelAndViewResolvers;
|
||||
|
||||
private HttpMessageConverter<?>[] messageConverters =
|
||||
new HttpMessageConverter[] {new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter(),
|
||||
new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter(),
|
||||
new FormHttpMessageConverter(), new SourceHttpMessageConverter()};
|
||||
|
||||
private ConfigurableBeanFactory beanFactory;
|
||||
|
|
@ -172,17 +174,16 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
private final Map<Class<?>, ServletHandlerMethodResolver> methodResolverCache =
|
||||
new ConcurrentHashMap<Class<?>, ServletHandlerMethodResolver>();
|
||||
|
||||
|
||||
public AnnotationMethodHandlerAdapter() {
|
||||
// no restriction of HTTP methods by default
|
||||
super(false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set if URL lookup should always use the full path within the current servlet context. Else, the path within the
|
||||
* current servlet mapping is used if applicable (that is, in the case of a ".../*" servlet mapping in web.xml).
|
||||
* <p>Default is "false".
|
||||
*
|
||||
* @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
|
||||
*/
|
||||
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
|
||||
|
|
@ -193,6 +194,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
* Set if context path and request URI should be URL-decoded. Both are returned <i>undecoded</i> by the Servlet API, in
|
||||
* contrast to the servlet path. <p>Uses either the request encoding or the default encoding according to the Servlet
|
||||
* spec (ISO-8859-1).
|
||||
*
|
||||
* @see org.springframework.web.util.UrlPathHelper#setUrlDecode
|
||||
*/
|
||||
public void setUrlDecode(boolean urlDecode) {
|
||||
|
|
@ -211,6 +213,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
/**
|
||||
* Set the PathMatcher implementation to use for matching URL paths against registered URL patterns. Default is
|
||||
* AntPathMatcher.
|
||||
*
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
|
|
@ -220,8 +223,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
|
||||
/**
|
||||
* Set the MethodNameResolver to use for resolving default handler methods (carrying an empty
|
||||
* <code>@RequestMapping</code> annotation).
|
||||
* <p>Will only kick in when the handler method cannot be resolved uniquely
|
||||
* <code>@RequestMapping</code> annotation). <p>Will only kick in when the handler method cannot be resolved uniquely
|
||||
* through the annotation metadata already.
|
||||
*/
|
||||
public void setMethodNameResolver(MethodNameResolver methodNameResolver) {
|
||||
|
|
@ -229,16 +231,15 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* Specify a WebBindingInitializer which will apply pre-configured configuration to every
|
||||
* DataBinder that this controller uses.
|
||||
* Specify a WebBindingInitializer which will apply pre-configured configuration to every DataBinder that this
|
||||
* controller uses.
|
||||
*/
|
||||
public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
|
||||
this.webBindingInitializer = webBindingInitializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the strategy to store session attributes with.
|
||||
* <p>Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},
|
||||
* Specify the strategy to store session attributes with. <p>Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},
|
||||
* storing session attributes in the HttpSession, using the same attribute name as in the model.
|
||||
*/
|
||||
public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
|
||||
|
|
@ -248,10 +249,10 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
|
||||
/**
|
||||
* Cache content produced by <code>@SessionAttributes</code> annotated handlers for the given number of seconds.
|
||||
* Default is 0, preventing caching completely.
|
||||
* <p>In contrast to the "cacheSeconds" property which will apply to all
|
||||
* Default is 0, preventing caching completely. <p>In contrast to the "cacheSeconds" property which will apply to all
|
||||
* general handlers (but not to <code>@SessionAttributes</code> annotated handlers), this setting will apply to
|
||||
* <code>@SessionAttributes</code> annotated handlers only.
|
||||
*
|
||||
* @see #setCacheSeconds
|
||||
* @see org.springframework.web.bind.annotation.SessionAttributes
|
||||
*/
|
||||
|
|
@ -260,17 +261,15 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* Set if controller execution should be synchronized on the session, to serialize
|
||||
* parallel invocations from the same client.
|
||||
* <p>More specifically, the execution of each handler method will get synchronized if this
|
||||
* flag is "true". The best available session mutex will be used for the synchronization;
|
||||
* ideally, this will be a mutex exposed by HttpSessionMutexListener.
|
||||
* <p>The session mutex is guaranteed to be the same object during the entire lifetime of the
|
||||
* session, available under the key defined by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant.
|
||||
* It serves as a safe reference to synchronize on for locking on the current session.
|
||||
* <p>In many cases, the HttpSession reference itself a safe mutex as well, since it will
|
||||
* always be the same object reference for the same active logical session. However, this is
|
||||
* not guaranteed across different servlet containers; the only 100% safe way is a session mutex.
|
||||
* Set if controller execution should be synchronized on the session, to serialize parallel invocations from the same
|
||||
* client. <p>More specifically, the execution of each handler method will get synchronized if this flag is "true". The
|
||||
* best available session mutex will be used for the synchronization; ideally, this will be a mutex exposed by
|
||||
* HttpSessionMutexListener. <p>The session mutex is guaranteed to be the same object during the entire lifetime of the
|
||||
* session, available under the key defined by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a safe
|
||||
* reference to synchronize on for locking on the current session. <p>In many cases, the HttpSession reference itself a
|
||||
* safe mutex as well, since it will always be the same object reference for the same active logical session. However,
|
||||
* this is not guaranteed across different servlet containers; the only 100% safe way is a session mutex.
|
||||
*
|
||||
* @see org.springframework.web.util.HttpSessionMutexListener
|
||||
* @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
|
||||
*/
|
||||
|
|
@ -280,40 +279,41 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
|
||||
/**
|
||||
* Set the ParameterNameDiscoverer to use for resolving method parameter names if needed (e.g. for default attribute
|
||||
* names).
|
||||
* <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
|
||||
* names). <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
|
||||
*/
|
||||
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom WebArgumentResolvers to use for special method parameter types. Such a custom WebArgumentResolver will kick
|
||||
* in first, having a chance to resolve an argument value before the standard argument handling kicks in.
|
||||
* Set a custom WebArgumentResolvers to use for special method parameter types. Such a custom WebArgumentResolver will
|
||||
* kick in first, having a chance to resolve an argument value before the standard argument handling kicks in.
|
||||
*/
|
||||
public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
|
||||
this.customArgumentResolvers = new WebArgumentResolver[]{argumentResolver};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more custom WebArgumentResolvers to use for special method parameter types. Any such custom WebArgumentResolver
|
||||
* will kick in first, having a chance to resolve an argument value before the standard argument handling kicks in.
|
||||
* Set one or more custom WebArgumentResolvers to use for special method parameter types. Any such custom
|
||||
* WebArgumentResolver will kick in first, having a chance to resolve an argument value before the standard argument
|
||||
* handling kicks in.
|
||||
*/
|
||||
public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
|
||||
this.customArgumentResolvers = argumentResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom ModelAndViewResolvers to use for special method return types. Such a custom ModelAndViewResolver will kick
|
||||
* in first, having a chance to resolve an return value before the standard ModelAndView handling kicks in.
|
||||
* Set a custom ModelAndViewResolvers to use for special method return types. Such a custom ModelAndViewResolver will
|
||||
* kick in first, having a chance to resolve an return value before the standard ModelAndView handling kicks in.
|
||||
*/
|
||||
public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) {
|
||||
this.customModelAndViewResolvers = new ModelAndViewResolver[]{customModelAndViewResolver};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more custom ModelAndViewResolvers to use for special method return types. Any such custom ModelAndViewResolver
|
||||
* will kick in first, having a chance to resolve an return value before the standard ModelAndView handling kicks in.
|
||||
* Set one or more custom ModelAndViewResolvers to use for special method return types. Any such custom
|
||||
* ModelAndViewResolver will kick in first, having a chance to resolve an return value before the standard ModelAndView
|
||||
* handling kicks in.
|
||||
*/
|
||||
public void setCustomModelAndViewResolvers(ModelAndViewResolver[] customModelAndViewResolvers) {
|
||||
this.customModelAndViewResolvers = customModelAndViewResolvers;
|
||||
|
|
@ -334,7 +334,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean supports(Object handler) {
|
||||
return getMethodResolver(handler).hasHandlerMethods();
|
||||
}
|
||||
|
|
@ -386,9 +385,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a HandlerMethodResolver for the given handler type.
|
||||
*/
|
||||
/** Build a HandlerMethodResolver for the given handler type. */
|
||||
private ServletHandlerMethodResolver getMethodResolver(Object handler) {
|
||||
Class handlerClass = ClassUtils.getUserClass(handler);
|
||||
ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
|
||||
|
|
@ -399,10 +396,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
return resolver;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Servlet-specific subclass of {@link HandlerMethodResolver}.
|
||||
*/
|
||||
/** Servlet-specific subclass of {@link HandlerMethodResolver}. */
|
||||
private class ServletHandlerMethodResolver extends HandlerMethodResolver {
|
||||
|
||||
private ServletHandlerMethodResolver(Class<?> handlerType) {
|
||||
|
|
@ -459,7 +453,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
else {
|
||||
for (RequestMethod requestMethod : mappingInfo.methods) {
|
||||
allowedMethods.add(requestMethod.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
|
|
@ -521,17 +515,10 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
/**
|
||||
* Determines the matched pattern for the given methodLevelPattern and path.
|
||||
*
|
||||
* <p>Uses the following algorithm:
|
||||
* <ol>
|
||||
* <li>If there is a type-level mapping with path information, it is
|
||||
* {@linkplain PathMatcher#combine(String, String) combined} with the method-level pattern.
|
||||
* <li>If there is a {@linkplain HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE best matching pattern} in the
|
||||
* request, it is combined with the method-level pattern.
|
||||
* <li>Otherwise,
|
||||
* @param methodLevelPattern
|
||||
* @param lookupPath
|
||||
* @param request
|
||||
* @return
|
||||
* <p>Uses the following algorithm: <ol> <li>If there is a type-level mapping with path information, it is {@linkplain
|
||||
* PathMatcher#combine(String, String) combined} with the method-level pattern. <li>If there is a {@linkplain
|
||||
* HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE best matching pattern} in the request, it is combined with the
|
||||
* method-level pattern. <li>Otherwise,
|
||||
*/
|
||||
private String getMatchedPattern(String methodLevelPattern, String lookupPath, HttpServletRequest request) {
|
||||
if (hasTypeLevelMapping() && (!ObjectUtils.isEmpty(getTypeLevelMapping().value()))) {
|
||||
|
|
@ -550,7 +537,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
String bestMatchingPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
||||
if (StringUtils.hasText(bestMatchingPattern)) {
|
||||
String combinedPattern = pathMatcher.combine(bestMatchingPattern, methodLevelPattern);
|
||||
if (!combinedPattern.equals(bestMatchingPattern) && (isPathMatchInternal(combinedPattern, lookupPath))) {
|
||||
if (!combinedPattern.equals(bestMatchingPattern) &&
|
||||
(isPathMatchInternal(combinedPattern, lookupPath))) {
|
||||
return combinedPattern;
|
||||
}
|
||||
}
|
||||
|
|
@ -610,10 +598,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Servlet-specific subclass of {@link HandlerMethodInvoker}.
|
||||
*/
|
||||
/** Servlet-specific subclass of {@link HandlerMethodInvoker}. */
|
||||
private class ServletHandlerMethodInvoker extends HandlerMethodInvoker {
|
||||
|
||||
private boolean responseArgumentUsed = false;
|
||||
|
|
@ -729,7 +714,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
Object returnValue,
|
||||
ExtendedModelMap implicitModel,
|
||||
ServletWebRequest webRequest) throws Exception {
|
||||
|
||||
|
||||
ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
|
||||
if (responseStatusAnn != null) {
|
||||
HttpStatus responseStatus = responseStatusAnn.value();
|
||||
|
|
@ -797,7 +782,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void handleResponseBody(Object returnValue, ServletWebRequest webRequest) throws ServletException, IOException {
|
||||
private void handleResponseBody(Object returnValue, ServletWebRequest webRequest)
|
||||
throws ServletException, IOException {
|
||||
HttpInputMessage inputMessage = new ServletServerHttpRequest(webRequest.getRequest());
|
||||
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
|
||||
if (acceptedMediaTypes.isEmpty()) {
|
||||
|
|
@ -809,16 +795,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
if (messageConverters != null) {
|
||||
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 (acceptedMediaType.includes(supportedMediaType)) {
|
||||
messageConverter.write(returnValue, outputMessage);
|
||||
this.responseArgumentUsed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (MediaType acceptedMediaType : acceptedMediaTypes) {
|
||||
if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
|
||||
messageConverter.write(returnValue, null, outputMessage);
|
||||
this.responseArgumentUsed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -827,7 +808,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static class RequestMappingInfo {
|
||||
|
||||
String[] paths = new String[0];
|
||||
|
|
@ -864,7 +844,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Comparator capable of sorting {@link RequestMappingInfo}s (RHIs) so that sorting a list with this comparator will
|
||||
* result in: <ul> <li>RHIs with {@linkplain RequestMappingInfo#matchedPaths better matched paths} take prescedence
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Iterator;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
|
@ -52,10 +52,10 @@ 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;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
||||
|
|
@ -63,14 +63,16 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
|
|||
import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletConfig;
|
||||
|
|
@ -79,9 +81,9 @@ import org.springframework.stereotype.Controller;
|
|||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.SerializationTestUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.FieldError;
|
||||
|
|
@ -445,7 +447,8 @@ public class ServletAnnotationControllerTests {
|
|||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
autoProxyCreator.setBeanFactory(wac.getBeanFactory());
|
||||
wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator);
|
||||
wac.getBeanFactory().registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
|
||||
wac.getBeanFactory()
|
||||
.registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
|
||||
wac.refresh();
|
||||
return wac;
|
||||
}
|
||||
|
|
@ -639,8 +642,8 @@ public class ServletAnnotationControllerTests {
|
|||
servlet.service(request, response);
|
||||
assertEquals("mySurpriseView", response.getContentAsString());
|
||||
|
||||
MyParameterDispatchingController deserialized = (MyParameterDispatchingController)
|
||||
SerializationTestUtils.serializeAndDeserialize(servlet.getWebApplicationContext().getBean("controller"));
|
||||
MyParameterDispatchingController deserialized = (MyParameterDispatchingController) SerializationTestUtils
|
||||
.serializeAndDeserialize(servlet.getWebApplicationContext().getBean("controller"));
|
||||
assertNotNull(deserialized.request);
|
||||
assertNotNull(deserialized.session);
|
||||
}
|
||||
|
|
@ -947,7 +950,23 @@ public class ServletAnnotationControllerTests {
|
|||
|
||||
@Test
|
||||
public void responseBodyNoAcceptableMediaType() throws ServletException, IOException {
|
||||
initServlet(RequestBodyController.class);
|
||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
||||
@Override
|
||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
||||
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||
wac.registerBeanDefinition("controller", new RootBeanDefinition(RequestBodyController.class));
|
||||
RootBeanDefinition converterDef = new RootBeanDefinition(StringHttpMessageConverter.class);
|
||||
converterDef.getPropertyValues().add("supportedMediaTypes", new MediaType("text", "plain"));
|
||||
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
|
||||
StringHttpMessageConverter converter = new StringHttpMessageConverter();
|
||||
converter.setSupportedMediaTypes(Collections.singletonList(new MediaType("text", "plain")));
|
||||
adapterDef.getPropertyValues().add("messageConverters", converter);
|
||||
wac.registerBeanDefinition("handlerAdapter", adapterDef);
|
||||
wac.refresh();
|
||||
return wac;
|
||||
}
|
||||
};
|
||||
servlet.init(new MockServletConfig());
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
|
||||
String requestBody = "Hello World";
|
||||
|
|
@ -975,7 +994,19 @@ public class ServletAnnotationControllerTests {
|
|||
|
||||
@Test
|
||||
public void unsupportedRequestBody() throws ServletException, IOException {
|
||||
initServlet(RequestBodyController.class);
|
||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
||||
@Override
|
||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
||||
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||
wac.registerBeanDefinition("controller", new RootBeanDefinition(RequestBodyController.class));
|
||||
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
|
||||
adapterDef.getPropertyValues().add("messageConverters", new ByteArrayHttpMessageConverter());
|
||||
wac.registerBeanDefinition("handlerAdapter", adapterDef);
|
||||
wac.refresh();
|
||||
return wac;
|
||||
}
|
||||
};
|
||||
servlet.init(new MockServletConfig());
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
|
||||
String requestBody = "Hello World";
|
||||
|
|
@ -1061,11 +1092,9 @@ public class ServletAnnotationControllerTests {
|
|||
@Override
|
||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
||||
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||
wac.registerBeanDefinition("controller",
|
||||
new RootBeanDefinition(ModelAndViewResolverController.class));
|
||||
wac.registerBeanDefinition("controller", new RootBeanDefinition(ModelAndViewResolverController.class));
|
||||
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
|
||||
adapterDef.getPropertyValues()
|
||||
.add("customModelAndViewResolver", new MyModelAndViewResolver());
|
||||
adapterDef.getPropertyValues().add("customModelAndViewResolver", new MyModelAndViewResolver());
|
||||
wac.registerBeanDefinition("handlerAdapter", adapterDef);
|
||||
wac.refresh();
|
||||
return wac;
|
||||
|
|
@ -1086,7 +1115,7 @@ public class ServletAnnotationControllerTests {
|
|||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test");
|
||||
request.setCookies(new Cookie("date", "2008-11-18"));
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals("test-108", response.getContentAsString());
|
||||
}
|
||||
|
|
@ -1096,7 +1125,7 @@ public class ServletAnnotationControllerTests {
|
|||
initServlet(AmbiguousParamsController.class);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals("noParams", response.getContentAsString());
|
||||
|
||||
|
|
@ -1116,7 +1145,6 @@ public class ServletAnnotationControllerTests {
|
|||
servlet.service(request, response);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void requestParamMap() throws Exception {
|
||||
initServlet(RequestParamMapController.class);
|
||||
|
|
@ -1229,11 +1257,9 @@ public class ServletAnnotationControllerTests {
|
|||
assertEquals("create", response.getContentAsString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Controllers
|
||||
*/
|
||||
* Controllers
|
||||
*/
|
||||
|
||||
@RequestMapping("/myPath.do")
|
||||
private static class MyController extends AbstractController {
|
||||
|
|
@ -1470,7 +1496,8 @@ public class ServletAnnotationControllerTests {
|
|||
@SuppressWarnings("unused")
|
||||
@ModelAttribute("myCommand")
|
||||
private ValidTestBean createTestBean(@RequestParam T defaultName,
|
||||
Map<String, Object> model, @RequestParam Date date) {
|
||||
Map<String, Object> model,
|
||||
@RequestParam Date date) {
|
||||
model.put("myKey", "myOriginalValue");
|
||||
ValidTestBean tb = new ValidTestBean();
|
||||
tb.setName(defaultName.getClass().getSimpleName() + ":" + defaultName.toString());
|
||||
|
|
@ -1740,9 +1767,10 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
List<TestBean> testBeans = (List<TestBean>) model.get("testBeanList");
|
||||
if (errors.hasFieldErrors("age")) {
|
||||
response.getWriter().write(viewName + "-" + tb.getName() + "-" +
|
||||
errors.getFieldError("age").getCode() + "-" + testBeans.get(0).getName() + "-" +
|
||||
model.get("myKey") + (model.containsKey("yourKey") ? "-" + model.get("yourKey") : ""));
|
||||
response.getWriter()
|
||||
.write(viewName + "-" + tb.getName() + "-" + errors.getFieldError("age").getCode() +
|
||||
"-" + testBeans.get(0).getName() + "-" + model.get("myKey") +
|
||||
(model.containsKey("yourKey") ? "-" + model.get("yourKey") : ""));
|
||||
}
|
||||
else {
|
||||
response.getWriter().write(viewName + "-" + tb.getName() + "-" + tb.getAge() + "-" +
|
||||
|
|
@ -1760,6 +1788,7 @@ public class ServletAnnotationControllerTests {
|
|||
public String getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
|
||||
request.getSession().setAttribute("model", model);
|
||||
}
|
||||
|
|
@ -1787,6 +1816,7 @@ public class ServletAnnotationControllerTests {
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Controller
|
||||
public @interface MyControllerAnnotation {
|
||||
|
||||
}
|
||||
|
||||
@MyControllerAnnotation
|
||||
|
|
@ -1835,8 +1865,8 @@ public class ServletAnnotationControllerTests {
|
|||
@RequestMapping("/myPath.do")
|
||||
public void myHandle(@RequestParam(value = "id", defaultValue = "${myKey}") String id,
|
||||
@RequestHeader(defaultValue = "#{systemProperties.myHeader}") String header,
|
||||
@Value("#{request.contextPath}") String contextPath, HttpServletResponse response)
|
||||
throws IOException {
|
||||
@Value("#{request.contextPath}") String contextPath,
|
||||
HttpServletResponse response) throws IOException {
|
||||
response.getWriter().write(String.valueOf(id) + "-" + String.valueOf(header) + "-" + contextPath);
|
||||
}
|
||||
}
|
||||
|
|
@ -1873,7 +1903,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
public static class PathOrderingController {
|
||||
|
||||
|
|
@ -1888,7 +1917,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
public static class RequestBodyController {
|
||||
|
||||
|
|
@ -1901,7 +1929,11 @@ public class ServletAnnotationControllerTests {
|
|||
|
||||
public static class MyMessageConverter implements HttpMessageConverter {
|
||||
|
||||
public boolean supports(Class clazz) {
|
||||
public boolean canRead(Class clazz, MediaType mediaType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean canWrite(Class clazz, MediaType mediaType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1914,7 +1946,7 @@ public class ServletAnnotationControllerTests {
|
|||
throw new HttpMessageNotReadableException("Could not read");
|
||||
}
|
||||
|
||||
public void write(Object o, HttpOutputMessage outputMessage)
|
||||
public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
throw new UnsupportedOperationException("Not implemented");
|
||||
}
|
||||
|
|
@ -1955,8 +1987,11 @@ public class ServletAnnotationControllerTests {
|
|||
|
||||
public static class MyModelAndViewResolver implements ModelAndViewResolver {
|
||||
|
||||
public ModelAndView resolveModelAndView(Method handlerMethod, Class handlerType,
|
||||
Object returnValue, ExtendedModelMap implicitModel, NativeWebRequest webRequest) {
|
||||
public ModelAndView resolveModelAndView(Method handlerMethod,
|
||||
Class handlerType,
|
||||
Object returnValue,
|
||||
ExtendedModelMap implicitModel,
|
||||
NativeWebRequest webRequest) {
|
||||
if (returnValue instanceof MySpecialArg) {
|
||||
return new ModelAndView(new View() {
|
||||
public String getContentType() {
|
||||
|
|
@ -2002,8 +2037,7 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
public void handle(@CookieValue("date") Date date, Writer writer)
|
||||
throws IOException {
|
||||
public void handle(@CookieValue("date") Date date, Writer writer) throws IOException {
|
||||
assertEquals("Invalid path variable value", new Date(108, 10, 18), date);
|
||||
writer.write("test-" + date.getYear());
|
||||
}
|
||||
|
|
@ -2043,7 +2077,8 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
|
||||
@RequestMapping("/multiValueMap")
|
||||
public void multiValueMap(@RequestParam MultiValueMap<String, String> params, Writer writer) throws IOException {
|
||||
public void multiValueMap(@RequestParam MultiValueMap<String, String> params, Writer writer)
|
||||
throws IOException {
|
||||
for (Iterator<Map.Entry<String, List<String>>> it1 = params.entrySet().iterator(); it1.hasNext();) {
|
||||
Map.Entry<String, List<String>> entry = it1.next();
|
||||
writer.write(entry.getKey() + "=[");
|
||||
|
|
@ -2078,7 +2113,8 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
|
||||
@RequestMapping("/multiValueMap")
|
||||
public void multiValueMap(@RequestHeader MultiValueMap<String, String> headers, Writer writer) throws IOException {
|
||||
public void multiValueMap(@RequestHeader MultiValueMap<String, String> headers, Writer writer)
|
||||
throws IOException {
|
||||
for (Iterator<Map.Entry<String, List<String>>> it1 = headers.entrySet().iterator(); it1.hasNext();) {
|
||||
Map.Entry<String, List<String>> entry = it1.next();
|
||||
writer.write(entry.getKey() + "=[");
|
||||
|
|
@ -2104,5 +2140,5 @@ public class ServletAnnotationControllerTests {
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,14 +56,22 @@ public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConv
|
|||
protected AbstractHttpMessageConverter() {
|
||||
}
|
||||
|
||||
/** Construct an {@code AbstractHttpMessageConverter} with one supported media type. */
|
||||
/**
|
||||
* Construct an {@code AbstractHttpMessageConverter} with one supported media type.
|
||||
*
|
||||
* @param supportedMediaType the supported media type
|
||||
*/
|
||||
protected AbstractHttpMessageConverter(MediaType supportedMediaType) {
|
||||
this.supportedMediaTypes = Collections.singletonList(supportedMediaType);
|
||||
setSupportedMediaTypes(Collections.singletonList(supportedMediaType));
|
||||
}
|
||||
|
||||
/** Construct an {@code AbstractHttpMessageConverter} with multiple supported media type. */
|
||||
/**
|
||||
* Construct an {@code AbstractHttpMessageConverter} with multiple supported media type.
|
||||
*
|
||||
* @param supportedMediaTypes the supported media types
|
||||
*/
|
||||
protected AbstractHttpMessageConverter(MediaType... supportedMediaTypes) {
|
||||
this.supportedMediaTypes = Arrays.asList(supportedMediaTypes);
|
||||
setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
|
||||
}
|
||||
|
||||
/** Set the list of {@link MediaType} objects supported by this converter. */
|
||||
|
|
@ -77,8 +85,59 @@ public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConv
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} <p>This implementation simple delegates to {@link #readInternal(Class, HttpInputMessage)}. Future
|
||||
* implementations might add some default behavior, however.
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>This implementation checks if the given class is {@linkplain #supports(Class) supported}, and if the {@linkplain
|
||||
* #getSupportedMediaTypes() supported media types} {@linkplain MediaType#includes(MediaType) include} the given media
|
||||
* type.
|
||||
*/
|
||||
public boolean canRead(Class<? extends T> clazz, MediaType mediaType) {
|
||||
return supports(clazz) && isSupported(mediaType);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>This implementation checks if the given class is {@linkplain #supports(Class) supported}, and if the {@linkplain
|
||||
* #getSupportedMediaTypes() supported media types} {@linkplain MediaType#includes(MediaType) include} the given media
|
||||
* type.
|
||||
*/
|
||||
public boolean canWrite(Class<? extends T> clazz, MediaType mediaType) {
|
||||
return supports(clazz) && isSupported(mediaType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any of the {@linkplain #setSupportedMediaTypes(List) supported media types} include the given media
|
||||
* type.
|
||||
*
|
||||
* @param mediaType the media type
|
||||
* @return true if the supported media types include the media type, or if the media type is {@code null}
|
||||
*/
|
||||
protected boolean isSupported(MediaType mediaType) {
|
||||
if (mediaType == null) {
|
||||
return true;
|
||||
}
|
||||
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
|
||||
if (supportedMediaType.includes(mediaType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the given class is supported by this converter.
|
||||
*
|
||||
* @param clazz the class to test for support
|
||||
* @return <code>true</code> if supported; <code>false</code> otherwise
|
||||
*/
|
||||
protected abstract boolean supports(Class<? extends T> clazz);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>This implementation simple delegates to {@link #readInternal(Class, HttpInputMessage)}. Future implementations
|
||||
* might add some default behavior, however.
|
||||
*/
|
||||
public final T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException {
|
||||
return readInternal(clazz, inputMessage);
|
||||
|
|
@ -97,17 +156,22 @@ public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConv
|
|||
throws IOException, HttpMessageNotReadableException;
|
||||
|
||||
/**
|
||||
* {@inheritDoc} <p>This implementation delegates to {@link #getContentType(Object)} and {@link
|
||||
* #getContentLength(Object)}, and sets the corresponding headers on the output message. It then calls {@link
|
||||
* #writeInternal(Object, HttpOutputMessage)}.
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>This implementation delegates to {@link #getDefaultContentType(Object)} if a content type was not provided, calls
|
||||
* {@link #getContentLength}, and sets the corresponding headers on the output message. It then calls {@link
|
||||
* #writeInternal}.
|
||||
*/
|
||||
public final void write(T t, HttpOutputMessage outputMessage) throws IOException {
|
||||
public final void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
HttpHeaders headers = outputMessage.getHeaders();
|
||||
MediaType contentType = getContentType(t);
|
||||
if (contentType == null) {
|
||||
contentType = getDefaultContentType(t);
|
||||
}
|
||||
if (contentType != null) {
|
||||
headers.setContentType(contentType);
|
||||
}
|
||||
Long contentLength = getContentLength(t);
|
||||
Long contentLength = getContentLength(t, contentType);
|
||||
if (contentLength != null) {
|
||||
headers.setContentLength(contentLength);
|
||||
}
|
||||
|
|
@ -116,30 +180,35 @@ public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConv
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the content type for the given type. <p>By default, this returns the first element of the {@link
|
||||
* #setSupportedMediaTypes(List) supportedMediaTypes} property, if any. Can be overriden in subclasses.
|
||||
* Returns the default content type for the given type. Called when {@link #write} is invoked without a specified
|
||||
* content type parameter.
|
||||
*
|
||||
* <p>By default, this returns the first element of the {@link #setSupportedMediaTypes(List) supportedMediaTypes}
|
||||
* property, if any. Can be overriden in subclasses.
|
||||
*
|
||||
* @param t the type to return the content type for
|
||||
* @return the content type, or <code>null</code> if not known
|
||||
*/
|
||||
protected MediaType getContentType(T t) {
|
||||
protected MediaType getDefaultContentType(T t) {
|
||||
List<MediaType> mediaTypes = getSupportedMediaTypes();
|
||||
return (!mediaTypes.isEmpty() ? mediaTypes.get(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content length for the given type. <p>By default, this returns <code>null</code>. Can be overriden in
|
||||
* Returns the content length for the given type.
|
||||
*
|
||||
* <p>By default, this returns {@code null}, meaning that the content length is unknown. Can be overriden in
|
||||
* subclasses.
|
||||
*
|
||||
* @param t the type to return the content length for
|
||||
* @return the content length, or <code>null</code> if not known
|
||||
* @return the content length, or {@code null} if not known
|
||||
*/
|
||||
protected Long getContentLength(T t) {
|
||||
protected Long getContentLength(T t, MediaType contentType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract template method that writes the actual body. Invoked from {@link #write(Object, HttpOutputMessage)}.
|
||||
* Abstract template method that writes the actual body. Invoked from {@link #write}.
|
||||
*
|
||||
* @param t the object to write to the output message
|
||||
* @param outputMessage the message to write to
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.imageio.IIOImage;
|
||||
|
|
@ -48,8 +49,7 @@ import org.springframework.util.Assert;
|
|||
* <p>By default, this converter can read all media types that are supported by the {@linkplain
|
||||
* ImageIO#getReaderMIMETypes() registered image readers}, and writes using the media type of the first available
|
||||
* {@linkplain javax.imageio.ImageIO#getWriterMIMETypes() registered image writer}. This behavior can be overriden by
|
||||
* setting the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes} and {@link
|
||||
* #setContentType(org.springframework.http.MediaType) contentType} properties respectively.
|
||||
* setting the #setContentType(org.springframework.http.MediaType) contentType} properties.
|
||||
*
|
||||
* <p>If the {@link #setCacheDir(java.io.File) cacheDir} property is set to an existing directory, this converter will
|
||||
* cache image data.
|
||||
|
|
@ -60,57 +60,50 @@ import org.springframework.util.Assert;
|
|||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
*/
|
||||
public class BufferedImageHttpMessageConverter extends AbstractHttpMessageConverter<BufferedImage> {
|
||||
public class BufferedImageHttpMessageConverter implements HttpMessageConverter<BufferedImage> {
|
||||
|
||||
private MediaType contentType;
|
||||
private List<MediaType> readableMediaTypes = new ArrayList<MediaType>();
|
||||
|
||||
private MediaType defaultContentType;
|
||||
|
||||
private File cacheDir;
|
||||
|
||||
public BufferedImageHttpMessageConverter() {
|
||||
String[] readerMediaTypes = ImageIO.getReaderMIMETypes();
|
||||
List<MediaType> supportedMediaTypes = new ArrayList<MediaType>(readerMediaTypes.length);
|
||||
for (String mediaType : readerMediaTypes) {
|
||||
supportedMediaTypes.add(MediaType.parseMediaType(mediaType));
|
||||
readableMediaTypes.add(MediaType.parseMediaType(mediaType));
|
||||
}
|
||||
setSupportedMediaTypes(supportedMediaTypes);
|
||||
|
||||
String[] writerMediaTypes = ImageIO.getWriterMIMETypes();
|
||||
if (writerMediaTypes.length > 0) {
|
||||
contentType = MediaType.parseMediaType(writerMediaTypes[0]);
|
||||
defaultContentType = MediaType.parseMediaType(writerMediaTypes[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link MediaType MediaTypes} supported for reading.
|
||||
* Returns the default {@code Content-Type} to be used for writing. Called when {@link #write} is invoked without a
|
||||
* specified content type parameter.
|
||||
*
|
||||
* @throws IllegalArgumentException if the given media type is not supported by the Java Image I/O API
|
||||
* @return the default content type
|
||||
*/
|
||||
@Override
|
||||
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
|
||||
Assert.notEmpty(supportedMediaTypes, "'supportedMediaTypes' must not be empty");
|
||||
for (MediaType supportedMediaType : supportedMediaTypes) {
|
||||
Iterator<ImageReader> imageReaders = ImageIO.getImageReadersByMIMEType(supportedMediaType.toString());
|
||||
if (!imageReaders.hasNext()) {
|
||||
throw new IllegalArgumentException(
|
||||
"MediaType [" + supportedMediaType + "] is not supported by the Java Image I/O API");
|
||||
}
|
||||
}
|
||||
super.setSupportedMediaTypes(supportedMediaTypes);
|
||||
public MediaType getDefaultContentType() {
|
||||
return defaultContentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Content-Type} to be used for writing.
|
||||
* Sets the default {@code Content-Type} to be used for writing.
|
||||
*
|
||||
* @throws IllegalArgumentException if the given content type is not supported by the Java Image I/O API
|
||||
*/
|
||||
public void setContentType(MediaType contentType) {
|
||||
Assert.notNull(contentType, "'contentType' must not be null");
|
||||
Iterator<ImageWriter> imageWriters = ImageIO.getImageWritersByMIMEType(contentType.toString());
|
||||
public void setDefaultContentType(MediaType defaultContentType) {
|
||||
Assert.notNull(defaultContentType, "'contentType' must not be null");
|
||||
Iterator<ImageWriter> imageWriters = ImageIO.getImageWritersByMIMEType(defaultContentType.toString());
|
||||
if (!imageWriters.hasNext()) {
|
||||
throw new IllegalArgumentException(
|
||||
"ContentType [" + contentType + "] is not supported by the Java Image I/O API");
|
||||
"ContentType [" + defaultContentType + "] is not supported by the Java Image I/O API");
|
||||
}
|
||||
|
||||
this.contentType = contentType;
|
||||
this.defaultContentType = defaultContentType;
|
||||
}
|
||||
|
||||
/** Sets the cache directory. If this property is set to an existing directory, this converter will cache image data. */
|
||||
|
|
@ -120,12 +113,46 @@ public class BufferedImageHttpMessageConverter extends AbstractHttpMessageConver
|
|||
this.cacheDir = cacheDir;
|
||||
}
|
||||
|
||||
public boolean supports(Class<? extends BufferedImage> clazz) {
|
||||
return BufferedImage.class.equals(clazz);
|
||||
public boolean canRead(Class<? extends BufferedImage> clazz, MediaType mediaType) {
|
||||
if (BufferedImage.class.equals(clazz)) {
|
||||
return isReadable(mediaType);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage readInternal(Class<BufferedImage> clazz, HttpInputMessage inputMessage) throws IOException {
|
||||
private boolean isReadable(MediaType mediaType) {
|
||||
if (mediaType == null) {
|
||||
return true;
|
||||
}
|
||||
Iterator<ImageReader> imageReaders = ImageIO.getImageReadersByMIMEType(mediaType.toString());
|
||||
return imageReaders.hasNext();
|
||||
}
|
||||
|
||||
public boolean canWrite(Class<? extends BufferedImage> clazz, MediaType mediaType) {
|
||||
if (BufferedImage.class.equals(clazz)) {
|
||||
return isWritable(mediaType);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isWritable(MediaType mediaType) {
|
||||
if (mediaType == null) {
|
||||
return true;
|
||||
}
|
||||
Iterator<ImageWriter> imageWriters = ImageIO.getImageWritersByMIMEType(mediaType.toString());
|
||||
return imageWriters.hasNext();
|
||||
}
|
||||
|
||||
public List<MediaType> getSupportedMediaTypes() {
|
||||
return Collections.unmodifiableList(readableMediaTypes);
|
||||
}
|
||||
|
||||
public BufferedImage read(Class<BufferedImage> clazz, HttpInputMessage inputMessage)
|
||||
throws IOException, HttpMessageNotReadableException {
|
||||
ImageInputStream imageInputStream = null;
|
||||
ImageReader imageReader = null;
|
||||
try {
|
||||
|
|
@ -168,13 +195,14 @@ public class BufferedImageHttpMessageConverter extends AbstractHttpMessageConver
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MediaType getContentType(BufferedImage image) {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeInternal(BufferedImage image, HttpOutputMessage outputMessage) throws IOException {
|
||||
public void write(BufferedImage image, MediaType contentType, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
if (contentType == null) {
|
||||
contentType = getDefaultContentType();
|
||||
}
|
||||
Assert.notNull(contentType,
|
||||
"Count not determine Content-Type, set one using the 'defaultContentType' property");
|
||||
outputMessage.getHeaders().setContentType(contentType);
|
||||
ImageOutputStream imageOutputStream = null;
|
||||
ImageWriter imageWriter = null;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -29,8 +29,7 @@ import org.springframework.util.FileCopyUtils;
|
|||
*
|
||||
* <p>By default, this converter supports all media types (<code>*/*</code>), and writes with a {@code
|
||||
* Content-Type} of {@code application/octet-stream}. This can be overridden by setting the {@link
|
||||
* #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property, and overridding {@link
|
||||
* #getContentType(byte[])}.
|
||||
* #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
|
|
@ -39,9 +38,10 @@ public class ByteArrayHttpMessageConverter extends AbstractHttpMessageConverter<
|
|||
|
||||
/** Creates a new instance of the {@code ByteArrayHttpMessageConverter}. */
|
||||
public ByteArrayHttpMessageConverter() {
|
||||
super(MediaType.ALL);
|
||||
super(new MediaType("application", "octet-stream"), MediaType.ALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<? extends byte[]> clazz) {
|
||||
return byte[].class.equals(clazz);
|
||||
}
|
||||
|
|
@ -60,12 +60,7 @@ public class ByteArrayHttpMessageConverter extends AbstractHttpMessageConverter<
|
|||
}
|
||||
|
||||
@Override
|
||||
protected MediaType getContentType(byte[] bytes) {
|
||||
return new MediaType("application", "octet-stream");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getContentLength(byte[] bytes) {
|
||||
protected Long getContentLength(byte[] bytes, MediaType contentType) {
|
||||
return (long) bytes.length;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ public class FormHttpMessageConverter extends AbstractHttpMessageConverter<Multi
|
|||
super(new MediaType("application", "x-www-form-urlencoded"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<? extends MultiValueMap<String, String>> clazz) {
|
||||
return MultiValueMap.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
|
@ -87,7 +88,7 @@ public class FormHttpMessageConverter extends AbstractHttpMessageConverter<Multi
|
|||
@Override
|
||||
protected void writeInternal(MultiValueMap<String, String> form, HttpOutputMessage outputMessage)
|
||||
throws IOException {
|
||||
MediaType contentType = getContentType(form);
|
||||
MediaType contentType = getDefaultContentType(form);
|
||||
Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Iterator<Map.Entry<String, List<String>>> entryIterator = form.entrySet().iterator();
|
||||
|
|
|
|||
|
|
@ -32,20 +32,35 @@ import org.springframework.http.MediaType;
|
|||
public interface HttpMessageConverter<T> {
|
||||
|
||||
/**
|
||||
* Indicate whether the given class is supported by this converter.
|
||||
* Indicates whether the given class can be read by this converter.
|
||||
*
|
||||
* @param clazz the class to test for support
|
||||
* @return <code>true</code> if supported; <code>false</code> otherwise
|
||||
* @param clazz the class to test for readability
|
||||
* @param mediaType the media type to read, can be {@code null} if not specified
|
||||
* @return <code>true</code> if readable; <code>false</code> otherwise
|
||||
*/
|
||||
boolean supports(Class<? extends T> clazz);
|
||||
boolean canRead(Class<? extends T> clazz, MediaType mediaType);
|
||||
|
||||
/** Return the list of {@link MediaType} objects supported by this converter. */
|
||||
/**
|
||||
* Indicates whether the given class can be written by this converter.
|
||||
*
|
||||
* @param clazz the class to test for writability
|
||||
* @param mediaType the media type to write, can be {@code null} if not specified
|
||||
* @return <code>true</code> if writable; <code>false</code> otherwise
|
||||
*/
|
||||
boolean canWrite(Class<? extends T> clazz, MediaType mediaType);
|
||||
|
||||
/**
|
||||
* Return the list of {@link MediaType} objects supported by this converter.
|
||||
*
|
||||
* @return the list of supported media types
|
||||
*/
|
||||
List<MediaType> getSupportedMediaTypes();
|
||||
|
||||
/**
|
||||
* Read an object of the given type form the given input message, and returns it.
|
||||
*
|
||||
* @param clazz the type of object to return
|
||||
* @param clazz the type of object to return. This type must have previously been passed to the {@link #canRead
|
||||
* canRead} method of this interface, which must have returned {@code true}.
|
||||
* @param inputMessage the HTTP input message to read from
|
||||
* @return the converted object
|
||||
* @throws IOException in case of I/O errors
|
||||
|
|
@ -56,11 +71,16 @@ public interface HttpMessageConverter<T> {
|
|||
/**
|
||||
* Write an given object to the given output message.
|
||||
*
|
||||
* @param t the object to write to the output message
|
||||
* @param t the object to write to the output message. The type of this object must have previously been passed to the
|
||||
* {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
|
||||
* @param contentType the content type to use when writing. May be {@code null} to indicate that the the default
|
||||
* content type of the converter must be used. If not {@code null}, this media type must have previously been passed to
|
||||
* the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
|
||||
* @param outputMessage the message to write to
|
||||
* @throws IOException in case of I/O errors
|
||||
* @throws HttpMessageNotWritableException in case of conversion errors
|
||||
*/
|
||||
void write(T t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
|
||||
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import org.springframework.util.FileCopyUtils;
|
|||
/**
|
||||
* Implementation of {@link HttpMessageConverter} that can read and write strings.
|
||||
*
|
||||
* <p>By default, this converter supports all text media types (<code>text/*</code>), and writes with a {@code
|
||||
* <p>By default, this converter supports all media types (<code>*/*</code>), and writes with a {@code
|
||||
* Content-Type} of {@code text/plain}. This can be overridden by setting the {@link
|
||||
* #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property.
|
||||
*
|
||||
|
|
@ -46,10 +46,11 @@ public class StringHttpMessageConverter extends AbstractHttpMessageConverter<Str
|
|||
private final List<Charset> availableCharsets;
|
||||
|
||||
public StringHttpMessageConverter() {
|
||||
super(new MediaType("text", "plain", DEFAULT_CHARSET), new MediaType("text", "*"));
|
||||
super(new MediaType("text", "plain", DEFAULT_CHARSET), MediaType.ALL);
|
||||
this.availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<? extends String> clazz) {
|
||||
return String.class.equals(clazz);
|
||||
}
|
||||
|
|
@ -62,9 +63,9 @@ public class StringHttpMessageConverter extends AbstractHttpMessageConverter<Str
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Long getContentLength(String s) {
|
||||
Charset charset = getContentType(s).getCharSet();
|
||||
if (charset != null) {
|
||||
protected Long getContentLength(String s, MediaType contentType) {
|
||||
if (contentType != null && contentType.getCharSet() != null) {
|
||||
Charset charset = contentType.getCharSet();
|
||||
try {
|
||||
return (long) s.getBytes(charset.name()).length;
|
||||
}
|
||||
|
|
@ -81,14 +82,15 @@ public class StringHttpMessageConverter extends AbstractHttpMessageConverter<Str
|
|||
@Override
|
||||
protected void writeInternal(String s, HttpOutputMessage outputMessage) throws IOException {
|
||||
outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
|
||||
MediaType contentType = getContentType(s);
|
||||
MediaType contentType = outputMessage.getHeaders().getContentType();
|
||||
Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET;
|
||||
FileCopyUtils.copy(s, new OutputStreamWriter(outputMessage.getBody(), charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of supported {@link Charset}. <p>By default, returns {@link Charset#availableCharsets()}. Can be
|
||||
* overridden in subclasses.
|
||||
* Return the list of supported {@link Charset}.
|
||||
*
|
||||
* <p>By default, returns {@link Charset#availableCharsets()}. Can be overridden in subclasses.
|
||||
*
|
||||
* @return the list of accepted charsets
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -54,9 +54,7 @@ public class MappingJacksonHttpMessageConverter<T> extends AbstractHttpMessageCo
|
|||
|
||||
private boolean prefixJson = false;
|
||||
|
||||
/**
|
||||
* Construct a new {@code BindingJacksonHttpMessageConverter},
|
||||
*/
|
||||
/** Construct a new {@code BindingJacksonHttpMessageConverter}, */
|
||||
public MappingJacksonHttpMessageConverter() {
|
||||
super(new MediaType("application", "json"));
|
||||
}
|
||||
|
|
@ -75,9 +73,7 @@ public class MappingJacksonHttpMessageConverter<T> extends AbstractHttpMessageCo
|
|||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code JsonEncoding} for this converter. By default, {@linkplain JsonEncoding#UTF8 UTF-8} is used.
|
||||
*/
|
||||
/** Sets the {@code JsonEncoding} for this converter. By default, {@linkplain JsonEncoding#UTF8 UTF-8} is used. */
|
||||
public void setEncoding(JsonEncoding encoding) {
|
||||
Assert.notNull(encoding, "'encoding' must not be null");
|
||||
this.encoding = encoding;
|
||||
|
|
@ -94,6 +90,7 @@ public class MappingJacksonHttpMessageConverter<T> extends AbstractHttpMessageCo
|
|||
this.prefixJson = prefixJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<? extends T> clazz) {
|
||||
return objectMapper.canSerialize(clazz);
|
||||
}
|
||||
|
|
@ -105,7 +102,7 @@ public class MappingJacksonHttpMessageConverter<T> extends AbstractHttpMessageCo
|
|||
}
|
||||
|
||||
@Override
|
||||
protected MediaType getContentType(T t) {
|
||||
protected MediaType getDefaultContentType(T t) {
|
||||
Charset charset = Charset.forName(encoding.getJavaName());
|
||||
return new MediaType("application", "json", charset);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ import org.springframework.http.converter.HttpMessageConversionException;
|
|||
* Abstract base class for {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverters} that
|
||||
* convert from/to XML.
|
||||
*
|
||||
* <p>By default, subclasses of this converter support {@code text/xml} and {@code application/xml}. This can be
|
||||
* overridden by setting the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property.
|
||||
* <p>By default, subclasses of this converter support {@code text/xml}, {@code application/xml}, and {@code
|
||||
* application/*-xml}. This can be overridden by setting the {@link #setSupportedMediaTypes(java.util.List)
|
||||
* supportedMediaTypes} property.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
|
|
@ -48,10 +49,10 @@ public abstract class AbstractXmlHttpMessageConverter<T> extends AbstractHttpMes
|
|||
|
||||
/**
|
||||
* Protected constructor that sets the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes} to {@code
|
||||
* text/xml} and {@code application/xml}.
|
||||
* text/xml} and {@code application/xml}, and {@code application/*-xml}.
|
||||
*/
|
||||
protected AbstractXmlHttpMessageConverter() {
|
||||
super(new MediaType("application", "xml"), new MediaType("text", "xml"));
|
||||
super(new MediaType("application", "xml"), new MediaType("text", "xml"), new MediaType("application", "*+xml"));
|
||||
}
|
||||
|
||||
/** Invokes {@link #readFromSource(Class, HttpHeaders, Source)}. */
|
||||
|
|
|
|||
|
|
@ -35,11 +35,19 @@ import org.springframework.http.converter.HttpMessageConversionException;
|
|||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
|
||||
/** @author Arjen Poutsma */
|
||||
/**
|
||||
* Implementation of {@link org.springframework.http.converter.HttpMessageConverter} that can read and write {@link
|
||||
* Source} objects.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SourceHttpMessageConverter<T extends Source> extends AbstractXmlHttpMessageConverter<T> {
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<? extends T> clazz) {
|
||||
return Source.class.isAssignableFrom(clazz);
|
||||
return DOMSource.class.equals(clazz) || SAXSource.class.equals(clazz) || StreamSource.class.equals(clazz) ||
|
||||
Source.class.equals(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -52,11 +60,11 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractXmlHtt
|
|||
return (T) new DOMSource(domResult.getNode());
|
||||
}
|
||||
else if (SAXSource.class.equals(clazz)) {
|
||||
ByteArrayInputStream bis = transformToByteArray(source);
|
||||
ByteArrayInputStream bis = transformToByteArrayInputStream(source);
|
||||
return (T) new SAXSource(new InputSource(bis));
|
||||
}
|
||||
else if (StreamSource.class.equals(clazz) || Source.class.equals(clazz)) {
|
||||
ByteArrayInputStream bis = transformToByteArray(source);
|
||||
ByteArrayInputStream bis = transformToByteArrayInputStream(source);
|
||||
return (T) new StreamSource(bis);
|
||||
}
|
||||
else {
|
||||
|
|
@ -65,11 +73,12 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractXmlHtt
|
|||
}
|
||||
}
|
||||
catch (TransformerException ex) {
|
||||
throw new HttpMessageNotReadableException("Could not transform from [" + source + "]", ex);
|
||||
throw new HttpMessageNotReadableException("Could not transform from [" + source + "] to [" + clazz + "]",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
private ByteArrayInputStream transformToByteArray(Source source) throws TransformerException {
|
||||
private ByteArrayInputStream transformToByteArrayInputStream(Source source) throws TransformerException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
transform(source, new StreamResult(bos));
|
||||
return new ByteArrayInputStream(bos.toByteArray());
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
|
@ -40,17 +40,17 @@ 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.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
|
|
@ -73,16 +73,16 @@ import org.springframework.web.context.request.NativeWebRequest;
|
|||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
/**
|
||||
* Support class for invoking an annotated handler method. Operates on the introspection results of a
|
||||
* {@link HandlerMethodResolver} for a specific handler type.
|
||||
* Support class for invoking an annotated handler method. Operates on the introspection results of a {@link
|
||||
* HandlerMethodResolver} for a specific handler type.
|
||||
*
|
||||
* <p>Used by {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} and
|
||||
* {@link org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter}.
|
||||
* <p>Used by {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} and {@link
|
||||
* org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @since 2.5.2
|
||||
* @see #invokeHandlerMethod
|
||||
* @since 2.5.2
|
||||
*/
|
||||
public class HandlerMethodInvoker {
|
||||
|
||||
|
|
@ -103,7 +103,6 @@ public class HandlerMethodInvoker {
|
|||
|
||||
private final SimpleSessionStatus sessionStatus = new SimpleSessionStatus();
|
||||
|
||||
|
||||
public HandlerMethodInvoker(HandlerMethodResolver methodResolver) {
|
||||
this(methodResolver, null);
|
||||
}
|
||||
|
|
@ -112,9 +111,12 @@ public class HandlerMethodInvoker {
|
|||
this(methodResolver, bindingInitializer, new DefaultSessionAttributeStore(), null, null, null);
|
||||
}
|
||||
|
||||
public HandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer,
|
||||
SessionAttributeStore sessionAttributeStore, ParameterNameDiscoverer parameterNameDiscoverer,
|
||||
WebArgumentResolver[] customArgumentResolvers, HttpMessageConverter[] messageConverters) {
|
||||
public HandlerMethodInvoker(HandlerMethodResolver methodResolver,
|
||||
WebBindingInitializer bindingInitializer,
|
||||
SessionAttributeStore sessionAttributeStore,
|
||||
ParameterNameDiscoverer parameterNameDiscoverer,
|
||||
WebArgumentResolver[] customArgumentResolvers,
|
||||
HttpMessageConverter[] messageConverters) {
|
||||
|
||||
this.methodResolver = methodResolver;
|
||||
this.bindingInitializer = bindingInitializer;
|
||||
|
|
@ -124,9 +126,10 @@ public class HandlerMethodInvoker {
|
|||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
|
||||
public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
|
||||
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
|
||||
public final Object invokeHandlerMethod(Method handlerMethod,
|
||||
Object handler,
|
||||
NativeWebRequest webRequest,
|
||||
ExtendedModelMap implicitModel) throws Exception {
|
||||
|
||||
Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
|
||||
try {
|
||||
|
|
@ -149,10 +152,10 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
Object attrValue = doInvokeMethod(attributeMethodToInvoke, handler, args);
|
||||
if ("".equals(attrName)) {
|
||||
Class resolvedType = GenericTypeResolver.resolveReturnType(
|
||||
attributeMethodToInvoke, handler.getClass());
|
||||
attrName = Conventions.getVariableNameForReturnType(
|
||||
attributeMethodToInvoke, resolvedType, attrValue);
|
||||
Class resolvedType =
|
||||
GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
|
||||
attrName =
|
||||
Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
|
||||
}
|
||||
if (!implicitModel.containsAttribute(attrName)) {
|
||||
implicitModel.addAttribute(attrName, attrValue);
|
||||
|
|
@ -171,8 +174,10 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
|
||||
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
|
||||
private Object[] resolveHandlerArguments(Method handlerMethod,
|
||||
Object handler,
|
||||
NativeWebRequest webRequest,
|
||||
ExtendedModelMap implicitModel) throws Exception {
|
||||
|
||||
Class[] paramTypes = handlerMethod.getParameterTypes();
|
||||
Object[] args = new Object[paramTypes.length];
|
||||
|
|
@ -287,7 +292,8 @@ public class HandlerMethodInvoker {
|
|||
args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
|
||||
}
|
||||
else if (attrName != null) {
|
||||
WebRequestDataBinder binder = resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
|
||||
WebRequestDataBinder binder =
|
||||
resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
|
||||
boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||
if (binder.getTarget() != null) {
|
||||
doBind(binder, webRequest, validate, !assignBindingResult);
|
||||
|
|
@ -334,8 +340,10 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
}
|
||||
|
||||
private Object[] resolveInitBinderArguments(Object handler, Method initBinderMethod,
|
||||
WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
|
||||
private Object[] resolveInitBinderArguments(Object handler,
|
||||
Method initBinderMethod,
|
||||
WebDataBinder binder,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
Class[] initBinderParams = initBinderMethod.getParameterTypes();
|
||||
Object[] initBinderArgs = new Object[initBinderParams.length];
|
||||
|
|
@ -390,8 +398,8 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
|
||||
if (paramName != null) {
|
||||
initBinderArgs[i] = resolveRequestParam(
|
||||
paramName, paramRequired, paramDefaultValue, methodParam, webRequest, null);
|
||||
initBinderArgs[i] =
|
||||
resolveRequestParam(paramName, paramRequired, paramDefaultValue, methodParam, webRequest, null);
|
||||
}
|
||||
else if (pathVarName != null) {
|
||||
initBinderArgs[i] = resolvePathVariable(pathVarName, methodParam, webRequest, null);
|
||||
|
|
@ -402,9 +410,12 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object resolveRequestParam(String paramName, boolean required, String defaultValue,
|
||||
MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
|
||||
throws Exception {
|
||||
private Object resolveRequestParam(String paramName,
|
||||
boolean required,
|
||||
String defaultValue,
|
||||
MethodParameter methodParam,
|
||||
NativeWebRequest webRequest,
|
||||
Object handlerForInitBinderCall) throws Exception {
|
||||
|
||||
Class<?> paramType = methodParam.getParameterType();
|
||||
if (Map.class.isAssignableFrom(paramType)) {
|
||||
|
|
@ -460,9 +471,12 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object resolveRequestHeader(String headerName, boolean required, String defaultValue,
|
||||
MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
|
||||
throws Exception {
|
||||
private Object resolveRequestHeader(String headerName,
|
||||
boolean required,
|
||||
String defaultValue,
|
||||
MethodParameter methodParam,
|
||||
NativeWebRequest webRequest,
|
||||
Object handlerForInitBinderCall) throws Exception {
|
||||
|
||||
Class<?> paramType = methodParam.getParameterType();
|
||||
if (Map.class.isAssignableFrom(paramType)) {
|
||||
|
|
@ -495,7 +509,8 @@ public class HandlerMethodInvoker {
|
|||
MultiValueMap<String, String> result;
|
||||
if (HttpHeaders.class.isAssignableFrom(mapType)) {
|
||||
result = new HttpHeaders();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
result = new LinkedMultiValueMap<String, String>();
|
||||
}
|
||||
for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
|
||||
|
|
@ -517,10 +532,7 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves the given {@link RequestBody @RequestBody} annotation.
|
||||
*/
|
||||
/** Resolves the given {@link RequestBody @RequestBody} annotation. */
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object resolveRequestBody(MethodParameter methodParam, NativeWebRequest webRequest, Object handler)
|
||||
throws Exception {
|
||||
|
|
@ -542,12 +554,8 @@ public class HandlerMethodInvoker {
|
|||
if (this.messageConverters != null) {
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
|
||||
if (messageConverter.supports(paramType)) {
|
||||
for (MediaType supportedMediaType : messageConverter.getSupportedMediaTypes()) {
|
||||
if (supportedMediaType.includes(contentType)) {
|
||||
return messageConverter.read(paramType, inputMessage);
|
||||
}
|
||||
}
|
||||
if (messageConverter.canRead(paramType, contentType)) {
|
||||
return messageConverter.read(paramType, inputMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -555,16 +563,19 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return a {@link HttpInputMessage} for the given {@link NativeWebRequest}.
|
||||
* Throws an UnsupportedOperationException by default.
|
||||
* Return 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 {
|
||||
private Object resolveCookieValue(String cookieName,
|
||||
boolean required,
|
||||
String defaultValue,
|
||||
MethodParameter methodParam,
|
||||
NativeWebRequest webRequest,
|
||||
Object handlerForInitBinderCall) throws Exception {
|
||||
|
||||
Class<?> paramType = methodParam.getParameterType();
|
||||
if (cookieName.length() == 0) {
|
||||
|
|
@ -585,18 +596,17 @@ public class HandlerMethodInvoker {
|
|||
return binder.convertIfNecessary(cookieValue, paramType, methodParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the given {@link CookieValue @CookieValue} annotation.
|
||||
* Throws an UnsupportedOperationException by default.
|
||||
*/
|
||||
/** Resolves the given {@link CookieValue @CookieValue} annotation. Throws an UnsupportedOperationException by default. */
|
||||
protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
|
||||
throws Exception {
|
||||
|
||||
throw new UnsupportedOperationException("@CookieValue not supported");
|
||||
}
|
||||
|
||||
private Object resolvePathVariable(String pathVarName, MethodParameter methodParam,
|
||||
NativeWebRequest webRequest, Object handlerForInitBinderCall) throws Exception {
|
||||
private Object resolvePathVariable(String pathVarName,
|
||||
MethodParameter methodParam,
|
||||
NativeWebRequest webRequest,
|
||||
Object handlerForInitBinderCall) throws Exception {
|
||||
|
||||
Class<?> paramType = methodParam.getParameterType();
|
||||
if (pathVarName.length() == 0) {
|
||||
|
|
@ -609,8 +619,8 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
|
||||
/**
|
||||
* Resolves the given {@link PathVariable @PathVariable} annotation.
|
||||
* Throws an UnsupportedOperationException by default.
|
||||
* Resolves the given {@link PathVariable @PathVariable} annotation. Throws an UnsupportedOperationException by
|
||||
* default.
|
||||
*/
|
||||
protected String resolvePathVariable(String pathVarName, Class paramType, NativeWebRequest webRequest)
|
||||
throws Exception {
|
||||
|
|
@ -621,9 +631,9 @@ public class HandlerMethodInvoker {
|
|||
private String getRequiredParameterName(MethodParameter methodParam) {
|
||||
String name = methodParam.getParameterName();
|
||||
if (name == null) {
|
||||
throw new IllegalStateException("No parameter name specified for argument of type [" +
|
||||
methodParam.getParameterType().getName() +
|
||||
"], and no parameter name information found in class file either.");
|
||||
throw new IllegalStateException(
|
||||
"No parameter name specified for argument of type [" + methodParam.getParameterType().getName() +
|
||||
"], and no parameter name information found in class file either.");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
|
@ -642,9 +652,11 @@ public class HandlerMethodInvoker {
|
|||
return value;
|
||||
}
|
||||
|
||||
private WebRequestDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
|
||||
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler)
|
||||
throws Exception {
|
||||
private WebRequestDataBinder resolveModelAttribute(String attrName,
|
||||
MethodParameter methodParam,
|
||||
ExtendedModelMap implicitModel,
|
||||
NativeWebRequest webRequest,
|
||||
Object handler) throws Exception {
|
||||
|
||||
// Bind request parameter onto object...
|
||||
String name = attrName;
|
||||
|
|
@ -671,8 +683,10 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void updateModelAttributes(Object handler, Map<String, Object> mavModel,
|
||||
ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception {
|
||||
public final void updateModelAttributes(Object handler,
|
||||
Map<String, Object> mavModel,
|
||||
ExtendedModelMap implicitModel,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
|
||||
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
|
||||
|
|
@ -731,15 +745,18 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
|
||||
protected void raiseMissingCookieException(String cookieName, Class paramType) throws Exception {
|
||||
throw new IllegalStateException("Missing cookie value '" + cookieName + "' of type [" + paramType.getName() + "]");
|
||||
throw new IllegalStateException(
|
||||
"Missing cookie value '" + cookieName + "' of type [" + paramType.getName() + "]");
|
||||
}
|
||||
|
||||
protected void raiseSessionRequiredException(String message) throws Exception {
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
|
||||
protected void doBind(WebRequestDataBinder binder, NativeWebRequest webRequest, boolean validate, boolean failOnErrors)
|
||||
throws Exception {
|
||||
protected void doBind(WebRequestDataBinder binder,
|
||||
NativeWebRequest webRequest,
|
||||
boolean validate,
|
||||
boolean failOnErrors) throws Exception {
|
||||
|
||||
binder.bind(webRequest);
|
||||
if (validate) {
|
||||
|
|
@ -785,9 +802,11 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
return WebArgumentResolver.UNRESOLVED;
|
||||
}
|
||||
|
||||
protected final void addReturnValueAsModelAttribute(
|
||||
Method handlerMethod, Class handlerType, Object returnValue, ExtendedModelMap implicitModel) {
|
||||
|
||||
protected final void addReturnValueAsModelAttribute(Method handlerMethod,
|
||||
Class handlerType,
|
||||
Object returnValue,
|
||||
ExtendedModelMap implicitModel) {
|
||||
|
||||
ModelAttribute attr = AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class);
|
||||
String attrName = (attr != null ? attr.value() : "");
|
||||
|
|
|
|||
|
|
@ -36,29 +36,28 @@ public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
|
|||
|
||||
private final Class<T> responseType;
|
||||
|
||||
private final List<HttpMessageConverter<T>> messageConverters;
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the {@code HttpMessageConverterExtractor} with the given response type and message
|
||||
* converters. The given converters must support the response type.
|
||||
*/
|
||||
public HttpMessageConverterExtractor(Class<T> responseType, List<HttpMessageConverter<T>> messageConverters) {
|
||||
public HttpMessageConverterExtractor(Class<T> responseType, List<HttpMessageConverter<?>> messageConverters) {
|
||||
Assert.notNull(responseType, "'responseType' must not be null");
|
||||
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
|
||||
this.responseType = responseType;
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T extractData(ClientHttpResponse response) throws IOException {
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
if (contentType == null) {
|
||||
throw new RestClientException("Cannot extract response: no Content-Type found");
|
||||
}
|
||||
for (HttpMessageConverter<T> messageConverter : messageConverters) {
|
||||
for (MediaType supportedMediaType : messageConverter.getSupportedMediaTypes()) {
|
||||
if (supportedMediaType.includes(contentType)) {
|
||||
return messageConverter.read(this.responseType, response);
|
||||
}
|
||||
for (HttpMessageConverter messageConverter : messageConverters) {
|
||||
if (messageConverter.canRead(responseType, contentType)) {
|
||||
return (T) messageConverter.read(this.responseType, response);
|
||||
}
|
||||
}
|
||||
throw new RestClientException(
|
||||
|
|
|
|||
|
|
@ -44,22 +44,18 @@ import org.springframework.web.util.UriTemplate;
|
|||
* enforces RESTful principles. It handles HTTP connections, leaving application code to provide URLs (with possible
|
||||
* template variables) and extract results.
|
||||
*
|
||||
* <p>The main entry points of this template are the methods named after the six main HTTP methods:
|
||||
* <table> <tr><th>HTTP
|
||||
* <p>The main entry points of this template are the methods named after the six main HTTP methods: <table> <tr><th>HTTP
|
||||
* method</th><th>RestTemplate methods</th></tr> <tr><td>DELETE</td><td>{@link #delete}</td></tr>
|
||||
* <tr><td>GET</td><td>{@link #getForObject}</td></tr>
|
||||
* <tr><td>HEAD</td><td>{@link #headForHeaders}</td></tr>
|
||||
* <tr><td>OPTIONS</td><td>{@link #optionsForAllow}</td></tr>
|
||||
* <tr><td>POST</td><td>{@link #postForLocation}</td></tr>
|
||||
* <tr><td></td><td>{@link #postForObject}</td></tr>
|
||||
* <tr><td>PUT</td><td>{@link #put}</td></tr> <tr><td>any</td><td>{@link #execute}</td></tr> </table>
|
||||
* <tr><td>GET</td><td>{@link #getForObject}</td></tr> <tr><td>HEAD</td><td>{@link #headForHeaders}</td></tr>
|
||||
* <tr><td>OPTIONS</td><td>{@link #optionsForAllow}</td></tr> <tr><td>POST</td><td>{@link #postForLocation}</td></tr>
|
||||
* <tr><td></td><td>{@link #postForObject}</td></tr> <tr><td>PUT</td><td>{@link #put}</td></tr>
|
||||
* <tr><td>any</td><td>{@link #execute}</td></tr> </table>
|
||||
*
|
||||
* <p>For each of these HTTP methods, there are three corresponding Java methods in the {@code RestTemplate}.
|
||||
* Two variant take a {@code String} URI as first argument (eg. {@link #getForObject(String, Class, String[])},
|
||||
* {@link #getForObject(String, Class, Map)}), and are capable of substituting any
|
||||
* {@linkplain UriTemplate URI templates} in that URL using either a
|
||||
* {@code String} variable arguments array, or a {@code Map<String, String>}. The string varargs variant expands the
|
||||
* given template variables in order, so that
|
||||
* <p>For each of these HTTP methods, there are three corresponding Java methods in the {@code RestTemplate}. Two
|
||||
* variant take a {@code String} URI as first argument (eg. {@link #getForObject(String, Class, String[])}, {@link
|
||||
* #getForObject(String, Class, Map)}), and are capable of substituting any {@linkplain UriTemplate URI templates} in
|
||||
* that URL using either a {@code String} variable arguments array, or a {@code Map<String, String>}. The string varargs
|
||||
* variant expands the given template variables in order, so that
|
||||
* <pre>
|
||||
* String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42",
|
||||
* "21");
|
||||
|
|
@ -71,22 +67,22 @@ import org.springframework.web.util.UriTemplate;
|
|||
* Map<String, String> vars = Collections.singletonMap("hotel", "42");
|
||||
* String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
|
||||
* </pre>
|
||||
* will perform a GET on {@code http://example.com/hotels/42/rooms/42}.
|
||||
* Alternatively, there are {@link URI} variant methods ({@link #getForObject(URI, Class)}), which do not allow for
|
||||
* URI templates, but allow you to reuse a single, expanded URI multiple times.
|
||||
* will perform a GET on {@code http://example.com/hotels/42/rooms/42}. Alternatively, there are {@link URI} variant
|
||||
* methods ({@link #getForObject(URI, Class)}), which do not allow for URI templates, but allow you to reuse a single,
|
||||
* expanded URI multiple times.
|
||||
*
|
||||
* <p>Furthermore, the {@code String}-argument methods assume that the URL String is unencoded. This means that
|
||||
* <pre>
|
||||
* restTemplate.getForObject("http://example.com/hotel list");
|
||||
* </pre>
|
||||
* will perform a GET on {@code http://example.com/hotel%20list}. As a result, any URL passed that is already encoded
|
||||
* will be encoded twice (i.e. {@code http://example.com/hotel%20list} will become {@code http://example.com/hotel%2520list}).
|
||||
* If this behavior is undesirable, use the {@code URI}-argument methods, which will not perform any URL encoding.
|
||||
* will be encoded twice (i.e. {@code http://example.com/hotel%20list} will become {@code
|
||||
* http://example.com/hotel%2520list}). If this behavior is undesirable, use the {@code URI}-argument methods, which
|
||||
* will not perform any URL encoding.
|
||||
*
|
||||
* <p>Objects passed to and returned from these methods are converted to and from HTTP messages by {@link
|
||||
* HttpMessageConverter} instances. Converters for the main mime types are registered by default, but you can also write
|
||||
* your own converter and register it via the {@link #setMessageConverters(HttpMessageConverter[]) messageConverters}
|
||||
* bean property.
|
||||
* your own converter and register it via the {@link #setMessageConverters messageConverters} bean property.
|
||||
*
|
||||
* <p>This template uses a {@link org.springframework.http.client.SimpleClientHttpRequestFactory} and a {@link
|
||||
* DefaultResponseErrorHandler} as default strategies for creating HTTP connections or handling HTTP errors,
|
||||
|
|
@ -104,14 +100,16 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
|
|||
|
||||
private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
|
||||
|
||||
private HttpMessageConverter<?>[] messageConverters =
|
||||
new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter(),
|
||||
new FormHttpMessageConverter(), new SourceHttpMessageConverter()};
|
||||
private List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
|
||||
|
||||
private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
|
||||
|
||||
/** Create a new instance of the {@link RestTemplate} using default settings. */
|
||||
public RestTemplate() {
|
||||
this.messageConverters.add(new ByteArrayHttpMessageConverter());
|
||||
this.messageConverters.add(new StringHttpMessageConverter());
|
||||
this.messageConverters.add(new FormHttpMessageConverter());
|
||||
this.messageConverters.add(new SourceHttpMessageConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -122,6 +120,7 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
|
|||
* @see org.springframework.http.client.CommonsClientHttpRequestFactory
|
||||
*/
|
||||
public RestTemplate(ClientHttpRequestFactory requestFactory) {
|
||||
this();
|
||||
setRequestFactory(requestFactory);
|
||||
}
|
||||
|
||||
|
|
@ -129,34 +128,16 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
|
|||
* 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) {
|
||||
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
/** Returns the message body converters. These converters are used to convert from and to HTTP requests and responses. */
|
||||
public HttpMessageConverter<?>[] getMessageConverters() {
|
||||
public List<HttpMessageConverter<?>> getMessageConverters() {
|
||||
return this.messageConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message body converters that support a particular type.
|
||||
*
|
||||
* @param type the type to return converters for
|
||||
* @return converters that support the given type
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> List<HttpMessageConverter<T>> getSupportedMessageConverters(Class<T> type) {
|
||||
HttpMessageConverter[] converters = getMessageConverters();
|
||||
List<HttpMessageConverter<T>> result = new ArrayList<HttpMessageConverter<T>>(converters.length);
|
||||
for (HttpMessageConverter converter : converters) {
|
||||
if (converter.supports(type)) {
|
||||
result.add((HttpMessageConverter<T>) converter);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Set the error handler. */
|
||||
public void setErrorHandler(ResponseErrorHandler errorHandler) {
|
||||
Assert.notNull(errorHandler, "'errorHandler' must not be null");
|
||||
|
|
@ -171,27 +152,25 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
|
|||
// GET
|
||||
|
||||
public <T> T getForObject(String url, Class<T> responseType, String... urlVariables) throws RestClientException {
|
||||
|
||||
checkForSupportedMessageConverter(responseType);
|
||||
List<HttpMessageConverter<T>> supportedMessageConverters = getSupportedMessageConverters(responseType);
|
||||
return execute(url, HttpMethod.GET, new AcceptHeaderRequestCallback<T>(supportedMessageConverters),
|
||||
new HttpMessageConverterExtractor<T>(responseType, supportedMessageConverters), urlVariables);
|
||||
AcceptHeaderRequestCallback<T> requestCallback = new AcceptHeaderRequestCallback<T>(responseType);
|
||||
HttpMessageConverterExtractor<T> responseExtractor =
|
||||
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
|
||||
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
|
||||
}
|
||||
|
||||
public <T> T getForObject(String url, Class<T> responseType, Map<String, String> urlVariables)
|
||||
throws RestClientException {
|
||||
|
||||
checkForSupportedMessageConverter(responseType);
|
||||
List<HttpMessageConverter<T>> supportedMessageConverters = getSupportedMessageConverters(responseType);
|
||||
return execute(url, HttpMethod.GET, new AcceptHeaderRequestCallback<T>(supportedMessageConverters),
|
||||
new HttpMessageConverterExtractor<T>(responseType, supportedMessageConverters), urlVariables);
|
||||
AcceptHeaderRequestCallback<T> requestCallback = new AcceptHeaderRequestCallback<T>(responseType);
|
||||
HttpMessageConverterExtractor<T> responseExtractor =
|
||||
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
|
||||
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
|
||||
}
|
||||
|
||||
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
|
||||
checkForSupportedMessageConverter(responseType);
|
||||
List<HttpMessageConverter<T>> supportedMessageConverters = getSupportedMessageConverters(responseType);
|
||||
return execute(url, HttpMethod.GET, new AcceptHeaderRequestCallback<T>(supportedMessageConverters),
|
||||
new HttpMessageConverterExtractor<T>(responseType, supportedMessageConverters));
|
||||
AcceptHeaderRequestCallback<T> requestCallback = new AcceptHeaderRequestCallback<T>(responseType);
|
||||
HttpMessageConverterExtractor<T> responseExtractor =
|
||||
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
|
||||
return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
|
||||
}
|
||||
|
||||
// HEAD
|
||||
|
|
@ -211,86 +190,62 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
|
|||
// POST
|
||||
|
||||
public URI postForLocation(String url, Object request, String... urlVariables) throws RestClientException {
|
||||
if (request != null) {
|
||||
checkForSupportedMessageConverter(request.getClass());
|
||||
}
|
||||
HttpHeaders headers =
|
||||
execute(url, HttpMethod.POST, new PostPutCallback(request), this.headersExtractor, urlVariables);
|
||||
PostPutCallback requestCallback = new PostPutCallback(request);
|
||||
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, this.headersExtractor, urlVariables);
|
||||
return headers.getLocation();
|
||||
}
|
||||
|
||||
public URI postForLocation(String url, Object request, Map<String, String> urlVariables)
|
||||
throws RestClientException {
|
||||
if (request != null) {
|
||||
checkForSupportedMessageConverter(request.getClass());
|
||||
}
|
||||
HttpHeaders headers =
|
||||
execute(url, HttpMethod.POST, new PostPutCallback(request), this.headersExtractor, urlVariables);
|
||||
PostPutCallback requestCallback = new PostPutCallback(request);
|
||||
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, this.headersExtractor, urlVariables);
|
||||
return headers.getLocation();
|
||||
}
|
||||
|
||||
public URI postForLocation(URI url, Object request)
|
||||
throws RestClientException {
|
||||
if (request != null) {
|
||||
checkForSupportedMessageConverter(request.getClass());
|
||||
}
|
||||
HttpHeaders headers = execute(url, HttpMethod.POST, new PostPutCallback(request), this.headersExtractor);
|
||||
public URI postForLocation(URI url, Object request) throws RestClientException {
|
||||
PostPutCallback requestCallback = new PostPutCallback(request);
|
||||
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, this.headersExtractor);
|
||||
return headers.getLocation();
|
||||
}
|
||||
|
||||
public <T> T postForObject(String url, Object request, Class<T> responseType, String... uriVariables)
|
||||
throws RestClientException {
|
||||
if (request != null) {
|
||||
checkForSupportedMessageConverter(request.getClass());
|
||||
}
|
||||
checkForSupportedMessageConverter(responseType);
|
||||
List<HttpMessageConverter<T>> responseMessageConverters = getSupportedMessageConverters(responseType);
|
||||
return execute(url, HttpMethod.POST, new PostPutCallback<T>(request, responseMessageConverters),
|
||||
new HttpMessageConverterExtractor<T>(responseType, responseMessageConverters), uriVariables);
|
||||
PostPutCallback<T> requestCallback = new PostPutCallback<T>(request, responseType);
|
||||
HttpMessageConverterExtractor<T> responseExtractor =
|
||||
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
|
||||
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
|
||||
}
|
||||
|
||||
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, String> uriVariables)
|
||||
throws RestClientException {
|
||||
if (request != null) {
|
||||
checkForSupportedMessageConverter(request.getClass());
|
||||
}
|
||||
checkForSupportedMessageConverter(responseType);
|
||||
List<HttpMessageConverter<T>> responseMessageConverters = getSupportedMessageConverters(responseType);
|
||||
return execute(url, HttpMethod.POST, new PostPutCallback<T>(request, responseMessageConverters),
|
||||
new HttpMessageConverterExtractor<T>(responseType, responseMessageConverters), uriVariables);
|
||||
PostPutCallback<T> requestCallback = new PostPutCallback<T>(request, responseType);
|
||||
HttpMessageConverterExtractor<T> responseExtractor =
|
||||
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
|
||||
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
|
||||
}
|
||||
|
||||
public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException {
|
||||
if (request != null) {
|
||||
checkForSupportedMessageConverter(request.getClass());
|
||||
}
|
||||
checkForSupportedMessageConverter(responseType);
|
||||
List<HttpMessageConverter<T>> responseMessageConverters = getSupportedMessageConverters(responseType);
|
||||
return execute(url, HttpMethod.POST, new PostPutCallback<T>(request, responseMessageConverters),
|
||||
new HttpMessageConverterExtractor<T>(responseType, responseMessageConverters));
|
||||
PostPutCallback<T> requestCallback = new PostPutCallback<T>(request, responseType);
|
||||
HttpMessageConverterExtractor<T> responseExtractor =
|
||||
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
|
||||
return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
|
||||
}
|
||||
|
||||
// PUT
|
||||
|
||||
public void put(String url, Object request, String... urlVariables) throws RestClientException {
|
||||
if (request != null) {
|
||||
checkForSupportedMessageConverter(request.getClass());
|
||||
}
|
||||
execute(url, HttpMethod.PUT, new PostPutCallback(request), null, urlVariables);
|
||||
PostPutCallback requestCallback = new PostPutCallback(request);
|
||||
execute(url, HttpMethod.PUT, requestCallback, null, urlVariables);
|
||||
}
|
||||
|
||||
public void put(String url, Object request, Map<String, String> urlVariables) throws RestClientException {
|
||||
if (request != null) {
|
||||
checkForSupportedMessageConverter(request.getClass());
|
||||
}
|
||||
execute(url, HttpMethod.PUT, new PostPutCallback(request), null, urlVariables);
|
||||
PostPutCallback requestCallback = new PostPutCallback(request);
|
||||
execute(url, HttpMethod.PUT, requestCallback, null, urlVariables);
|
||||
}
|
||||
|
||||
public void put(URI url, Object request) throws RestClientException {
|
||||
if (request != null) {
|
||||
checkForSupportedMessageConverter(request.getClass());
|
||||
}
|
||||
execute(url, HttpMethod.PUT, new PostPutCallback(request), null);
|
||||
PostPutCallback requestCallback = new PostPutCallback(request);
|
||||
execute(url, HttpMethod.PUT, requestCallback, null);
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
|
@ -405,28 +360,12 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether any of the registered {@linkplain #setMessageConverters(HttpMessageConverter[]) message body
|
||||
* converters} can convert the given type.
|
||||
*
|
||||
* @param type the type to check for
|
||||
* @throws IllegalArgumentException if no supported entity converter can be found
|
||||
* @see HttpMessageConverter#supports(Class)
|
||||
*/
|
||||
private void checkForSupportedMessageConverter(Class type) {
|
||||
for (HttpMessageConverter<?> entityConverter : getMessageConverters()) {
|
||||
if (entityConverter.supports(type)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Could not resolve HttpMessageConverter for [" + type.getName() + "]");
|
||||
}
|
||||
|
||||
private void logResponseStatus(HttpMethod method, URI url, ClientHttpResponse response) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
try {
|
||||
logger.debug(method.name() + " request for \"" + url + "\" resulted in " + response.getStatusCode() +
|
||||
" (" + response.getStatusText() + ")");
|
||||
logger.debug(
|
||||
method.name() + " request for \"" + url + "\" resulted in " + response.getStatusCode() + " (" +
|
||||
response.getStatusText() + ")");
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore
|
||||
|
|
@ -437,8 +376,9 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
|
|||
private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException {
|
||||
if (logger.isWarnEnabled()) {
|
||||
try {
|
||||
logger.warn(method.name() + " request for \"" + url + "\" resulted in " + response.getStatusCode() +
|
||||
" (" + response.getStatusText() + "); invoking error handler");
|
||||
logger.warn(
|
||||
method.name() + " request for \"" + url + "\" resulted in " + response.getStatusCode() + " (" +
|
||||
response.getStatusText() + "); invoking error handler");
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore
|
||||
|
|
@ -450,27 +390,36 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
|
|||
/** Request callback implementation that prepares the request's accept headers. */
|
||||
private class AcceptHeaderRequestCallback<T> implements RequestCallback {
|
||||
|
||||
private final List<HttpMessageConverter<T>> messageConverters;
|
||||
private final Class<T> responseType;
|
||||
|
||||
private AcceptHeaderRequestCallback(List<HttpMessageConverter<T>> messageConverters) {
|
||||
this.messageConverters = messageConverters;
|
||||
private AcceptHeaderRequestCallback() {
|
||||
responseType = null;
|
||||
}
|
||||
|
||||
private AcceptHeaderRequestCallback(Class<T> responseType) {
|
||||
this.responseType = responseType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void doWithRequest(ClientHttpRequest request) throws IOException {
|
||||
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
|
||||
for (HttpMessageConverter<?> entityConverter : messageConverters) {
|
||||
List<MediaType> supportedMediaTypes = entityConverter.getSupportedMediaTypes();
|
||||
for (MediaType supportedMediaType : supportedMediaTypes) {
|
||||
if (supportedMediaType.getCharSet() != null) {
|
||||
supportedMediaType =
|
||||
new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype());
|
||||
if (responseType != null) {
|
||||
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
|
||||
for (HttpMessageConverter messageConverter : getMessageConverters()) {
|
||||
if (messageConverter.canRead(responseType, null)) {
|
||||
List<MediaType> supportedMediaTypes = messageConverter.getSupportedMediaTypes();
|
||||
for (MediaType supportedMediaType : supportedMediaTypes) {
|
||||
if (supportedMediaType.getCharSet() != null) {
|
||||
supportedMediaType =
|
||||
new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype());
|
||||
}
|
||||
allSupportedMediaTypes.add(supportedMediaType);
|
||||
}
|
||||
}
|
||||
allSupportedMediaTypes.add(supportedMediaType);
|
||||
}
|
||||
}
|
||||
if (!allSupportedMediaTypes.isEmpty()) {
|
||||
Collections.sort(allSupportedMediaTypes);
|
||||
request.getHeaders().setAccept(allSupportedMediaTypes);
|
||||
if (!allSupportedMediaTypes.isEmpty()) {
|
||||
Collections.sort(allSupportedMediaTypes);
|
||||
request.getHeaders().setAccept(allSupportedMediaTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -478,25 +427,45 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
|
|||
/** Request callback implementation that writes the given object to the request stream. */
|
||||
private class PostPutCallback<T> extends AcceptHeaderRequestCallback<T> {
|
||||
|
||||
private final Object request;
|
||||
private final Object requestBody;
|
||||
|
||||
private PostPutCallback(Object request, List<HttpMessageConverter<T>> responseMessageConverters) {
|
||||
super(responseMessageConverters);
|
||||
this.request = request;
|
||||
private final MediaType requestContentType;
|
||||
|
||||
private PostPutCallback(Object requestBody) {
|
||||
this.requestBody = requestBody;
|
||||
this.requestContentType = null;
|
||||
}
|
||||
|
||||
private PostPutCallback(Object request) {
|
||||
super(Collections.<HttpMessageConverter<T>>emptyList());
|
||||
this.request = request;
|
||||
private PostPutCallback(Object requestBody, Class<T> responseType) {
|
||||
super(responseType);
|
||||
this.requestBody = requestBody;
|
||||
this.requestContentType = null;
|
||||
}
|
||||
|
||||
private PostPutCallback(Object requestBody, MediaType requestContentType, Class<T> responseType) {
|
||||
super(responseType);
|
||||
this.requestBody = requestBody;
|
||||
this.requestContentType = requestContentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
|
||||
super.doWithRequest(httpRequest);
|
||||
if (request != null) {
|
||||
HttpMessageConverter entityConverter = getSupportedMessageConverters(this.request.getClass()).get(0);
|
||||
entityConverter.write(this.request, httpRequest);
|
||||
if (requestBody != null) {
|
||||
Class requestType = requestBody.getClass();
|
||||
for (HttpMessageConverter messageConverter : getMessageConverters()) {
|
||||
if (messageConverter.canWrite(requestType, requestContentType)) {
|
||||
messageConverter.write(requestBody, requestContentType, httpRequest);
|
||||
return;
|
||||
}
|
||||
}
|
||||
String message = "Could not write request: no suitable HttpMessageConverter found for request type [" +
|
||||
requestType.getName() + "]";
|
||||
if (requestContentType != null) {
|
||||
message += " and content type [" + requestContentType + "]";
|
||||
}
|
||||
throw new RestClientException(message);
|
||||
}
|
||||
else {
|
||||
httpRequest.getHeaders().setContentLength(0L);
|
||||
|
|
|
|||
|
|
@ -42,8 +42,15 @@ public class BufferedImageHttpMessageConverterTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void supports() {
|
||||
assertTrue("Image not supported", converter.supports(BufferedImage.class));
|
||||
public void canRead() {
|
||||
assertTrue("Image not supported", converter.canRead(BufferedImage.class, null));
|
||||
assertTrue("Image not supported", converter.canRead(BufferedImage.class, new MediaType("image", "png")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canWrite() {
|
||||
assertTrue("Image not supported", converter.canWrite(BufferedImage.class, null));
|
||||
assertTrue("Image not supported", converter.canWrite(BufferedImage.class, new MediaType("image", "png")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -60,11 +67,25 @@ public class BufferedImageHttpMessageConverterTests {
|
|||
@Test
|
||||
public void write() throws IOException {
|
||||
Resource logo = new ClassPathResource("logo.jpg", BufferedImageHttpMessageConverterTests.class);
|
||||
MediaType contentType = new MediaType("image", "png");
|
||||
converter.setContentType(contentType);
|
||||
BufferedImage body = ImageIO.read(logo.getFile());
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
converter.write(body, outputMessage);
|
||||
MediaType contentType = new MediaType("image", "png");
|
||||
converter.write(body, contentType, outputMessage);
|
||||
assertEquals("Invalid content type", contentType, outputMessage.getHeaders().getContentType());
|
||||
assertTrue("Invalid size", outputMessage.getBodyAsBytes().length > 0);
|
||||
BufferedImage result = ImageIO.read(new ByteArrayInputStream(outputMessage.getBodyAsBytes()));
|
||||
assertEquals("Invalid height", 500, result.getHeight());
|
||||
assertEquals("Invalid width", 750, result.getWidth());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeDefaultContentType() throws IOException {
|
||||
Resource logo = new ClassPathResource("logo.jpg", BufferedImageHttpMessageConverterTests.class);
|
||||
MediaType contentType = new MediaType("image", "png");
|
||||
converter.setDefaultContentType(contentType);
|
||||
BufferedImage body = ImageIO.read(logo.getFile());
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
converter.write(body, contentType, outputMessage);
|
||||
assertEquals("Invalid content type", contentType, outputMessage.getHeaders().getContentType());
|
||||
assertTrue("Invalid size", outputMessage.getBodyAsBytes().length > 0);
|
||||
BufferedImage result = ImageIO.read(new ByteArrayInputStream(outputMessage.getBodyAsBytes()));
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ import org.springframework.http.MediaType;
|
|||
import org.springframework.http.MockHttpInputMessage;
|
||||
import org.springframework.http.MockHttpOutputMessage;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
/** @author Arjen Poutsma */
|
||||
public class ByteArrayHttpMessageConverterTests {
|
||||
|
||||
private ByteArrayHttpMessageConverter converter;
|
||||
|
|
@ -51,7 +49,7 @@ public class ByteArrayHttpMessageConverterTests {
|
|||
public void write() throws IOException {
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
byte[] body = new byte[]{0x1, 0x2};
|
||||
converter.write(body, outputMessage);
|
||||
converter.write(body, null, outputMessage);
|
||||
assertArrayEquals("Invalid result", body, outputMessage.getBodyAsBytes());
|
||||
assertEquals("Invalid content-type", new MediaType("application", "octet-stream"),
|
||||
outputMessage.getHeaders().getContentType());
|
||||
|
|
|
|||
|
|
@ -30,9 +30,7 @@ import org.springframework.http.MockHttpOutputMessage;
|
|||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
/** @author Arjen Poutsma */
|
||||
public class FormHttpMessageConverterTests {
|
||||
|
||||
private FormHttpMessageConverter converter;
|
||||
|
|
@ -67,7 +65,7 @@ public class FormHttpMessageConverterTests {
|
|||
body.add("name 2", "value 2+2");
|
||||
body.add("name 3", null);
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
converter.write(body, outputMessage);
|
||||
converter.write(body, null, outputMessage);
|
||||
Charset iso88591 = Charset.forName("ISO-8859-1");
|
||||
assertEquals("Invalid result", "name+1=value+1&name+2=value+2%2B1&name+2=value+2%2B2&name+3",
|
||||
outputMessage.getBodyAsString(iso88591));
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package org.springframework.http.converter;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
|
|
@ -28,9 +27,7 @@ import org.springframework.http.MediaType;
|
|||
import org.springframework.http.MockHttpInputMessage;
|
||||
import org.springframework.http.MockHttpOutputMessage;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
/** @author Arjen Poutsma */
|
||||
public class StringHttpMessageConverterTests {
|
||||
|
||||
private StringHttpMessageConverter converter;
|
||||
|
|
@ -55,7 +52,7 @@ public class StringHttpMessageConverterTests {
|
|||
Charset iso88591 = Charset.forName("ISO-8859-1");
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
String body = "H\u00e9llo W\u00f6rld";
|
||||
converter.write(body, outputMessage);
|
||||
converter.write(body, null, outputMessage);
|
||||
assertEquals("Invalid result", body, outputMessage.getBodyAsString(iso88591));
|
||||
assertEquals("Invalid content-type", new MediaType("text", "plain", iso88591),
|
||||
outputMessage.getHeaders().getContentType());
|
||||
|
|
@ -67,13 +64,12 @@ public class StringHttpMessageConverterTests {
|
|||
@Test
|
||||
public void writeUTF8() throws IOException {
|
||||
Charset utf8 = Charset.forName("UTF-8");
|
||||
converter.setSupportedMediaTypes(Collections.singletonList(new MediaType("text", "plain", utf8)));
|
||||
MediaType contentType = new MediaType("text", "plain", utf8);
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
String body = "H\u00e9llo W\u00f6rld";
|
||||
converter.write(body, outputMessage);
|
||||
converter.write(body, contentType, outputMessage);
|
||||
assertEquals("Invalid result", body, outputMessage.getBodyAsString(utf8));
|
||||
assertEquals("Invalid content-type", new MediaType("text", "plain", utf8),
|
||||
outputMessage.getHeaders().getContentType());
|
||||
assertEquals("Invalid content-type", contentType, outputMessage.getHeaders().getContentType());
|
||||
assertEquals("Invalid content-length", body.getBytes(utf8).length,
|
||||
outputMessage.getHeaders().getContentLength());
|
||||
assertFalse("Invalid accept-charset", outputMessage.getHeaders().getAcceptCharset().isEmpty());
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public class MappingJacksonHttpMessageConverterTests {
|
|||
public void setUp() {
|
||||
converter = new MappingJacksonHttpMessageConverter<MyBean>();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void supports() {
|
||||
assertTrue(converter.supports(MyBean.class));
|
||||
|
|
@ -68,10 +68,10 @@ public class MappingJacksonHttpMessageConverterTests {
|
|||
"{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
|
||||
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
|
||||
inputMessage.getHeaders().setContentType(new MediaType("application", "json"));
|
||||
HashMap<String,Object> result = converter.read(HashMap.class, inputMessage);
|
||||
HashMap<String, Object> result = converter.read(HashMap.class, inputMessage);
|
||||
assertEquals("Foo", result.get("string"));
|
||||
assertEquals(42, result.get("number"));
|
||||
assertEquals(42D, (Double)result.get("fraction"), 0D);
|
||||
assertEquals(42D, (Double) result.get("fraction"), 0D);
|
||||
List array = new ArrayList();
|
||||
array.add("Foo");
|
||||
array.add("Bar");
|
||||
|
|
@ -90,7 +90,7 @@ public class MappingJacksonHttpMessageConverterTests {
|
|||
body.setArray(new String[]{"Foo", "Bar"});
|
||||
body.setBool(true);
|
||||
body.setBytes(new byte[]{0x1, 0x2});
|
||||
converter.write(body, outputMessage);
|
||||
converter.write(body, null, outputMessage);
|
||||
Charset utf8 = Charset.forName("UTF-8");
|
||||
String result = outputMessage.getBodyAsString(utf8);
|
||||
assertTrue(result.contains("\"string\":\"Foo\""));
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public class MarshallingHttpMessageConverterTests {
|
|||
marshaller.marshal(eq(body), isA(StreamResult.class));
|
||||
|
||||
replay(marshaller, unmarshaller);
|
||||
converter.write(body, outputMessage);
|
||||
converter.write(body, null, outputMessage);
|
||||
assertEquals("Invalid content-type", new MediaType("application", "xml"),
|
||||
outputMessage.getHeaders().getContentType());
|
||||
verify(marshaller, unmarshaller);
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ public class SourceHttpMessageConverterTests {
|
|||
|
||||
SourceHttpMessageConverter<Source> converter = new SourceHttpMessageConverter<Source>();
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
converter.write(domSource, outputMessage);
|
||||
converter.write(domSource, null, outputMessage);
|
||||
assertXMLEqual("Invalid result", "<root>Hello World</root>",
|
||||
outputMessage.getBodyAsString(Charset.forName("UTF-8")));
|
||||
assertEquals("Invalid content-type", new MediaType("application", "xml"),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import java.io.IOException;
|
|||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
|
@ -36,9 +35,7 @@ import org.springframework.http.MediaType;
|
|||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
|
||||
/** @author Arjen Poutsma */
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -65,18 +62,7 @@ public class RestTemplateTests {
|
|||
converter = createMock(HttpMessageConverter.class);
|
||||
template = new RestTemplate(requestFactory);
|
||||
template.setErrorHandler(errorHandler);
|
||||
template.setMessageConverters(new HttpMessageConverter<?>[]{converter});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSupportedMessageBodyConverters() {
|
||||
ByteArrayHttpMessageConverter byteArrayConverter = new ByteArrayHttpMessageConverter();
|
||||
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
|
||||
template.setMessageConverters(new HttpMessageConverter<?>[]{byteArrayConverter, stringConverter});
|
||||
|
||||
List<HttpMessageConverter<String>> result = template.getSupportedMessageConverters(String.class);
|
||||
assertEquals("Invalid amount of String converters", 1, result.size());
|
||||
assertEquals("Invalid String converters", stringConverter, result.get(0));
|
||||
template.setMessageConverters(Collections.<HttpMessageConverter<?>>singletonList(converter));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -136,9 +122,9 @@ public class RestTemplateTests {
|
|||
|
||||
@Test
|
||||
public void getForObject() throws Exception {
|
||||
expect(converter.supports(String.class)).andReturn(true).times(2);
|
||||
expect(converter.canRead(String.class, null)).andReturn(true);
|
||||
MediaType textPlain = new MediaType("text", "plain");
|
||||
expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(textPlain)).times(2);
|
||||
expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(textPlain));
|
||||
expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.GET)).andReturn(request);
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
expect(request.getHeaders()).andReturn(requestHeaders);
|
||||
|
|
@ -147,6 +133,7 @@ public class RestTemplateTests {
|
|||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setContentType(textPlain);
|
||||
expect(response.getHeaders()).andReturn(responseHeaders);
|
||||
expect(converter.canRead(String.class, textPlain)).andReturn(true);
|
||||
String expected = "Hello World";
|
||||
expect(converter.read(String.class, response)).andReturn(expected);
|
||||
response.close();
|
||||
|
|
@ -160,28 +147,11 @@ public class RestTemplateTests {
|
|||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getForObjectUnsupportedClass() throws Exception {
|
||||
expect(converter.supports(String.class)).andReturn(false);
|
||||
|
||||
replayMocks();
|
||||
|
||||
try {
|
||||
template.getForObject("http://example.com/{p}", String.class, "resource");
|
||||
fail("IllegalArgumentException expected");
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
// expected
|
||||
}
|
||||
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getUnsupportedMediaType() throws Exception {
|
||||
expect(converter.supports(String.class)).andReturn(true).times(2);
|
||||
expect(converter.canRead(String.class, null)).andReturn(true);
|
||||
MediaType supportedMediaType = new MediaType("foo", "bar");
|
||||
expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(supportedMediaType)).times(2);
|
||||
expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(supportedMediaType));
|
||||
expect(requestFactory.createRequest(new URI("http://example.com/resource"), HttpMethod.GET)).andReturn(request);
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
expect(request.getHeaders()).andReturn(requestHeaders);
|
||||
|
|
@ -191,6 +161,7 @@ public class RestTemplateTests {
|
|||
MediaType contentType = new MediaType("bar", "baz");
|
||||
responseHeaders.setContentType(contentType);
|
||||
expect(response.getHeaders()).andReturn(responseHeaders);
|
||||
expect(converter.canRead(String.class, contentType)).andReturn(false);
|
||||
response.close();
|
||||
|
||||
replayMocks();
|
||||
|
|
@ -224,10 +195,10 @@ public class RestTemplateTests {
|
|||
|
||||
@Test
|
||||
public void postForLocation() throws Exception {
|
||||
expect(converter.supports(String.class)).andReturn(true).times(2);
|
||||
expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).andReturn(request);
|
||||
String helloWorld = "Hello World";
|
||||
converter.write(helloWorld, request);
|
||||
expect(converter.canWrite(String.class, null)).andReturn(true);
|
||||
converter.write(helloWorld, null, request);
|
||||
expect(request.execute()).andReturn(response);
|
||||
expect(errorHandler.hasError(response)).andReturn(false);
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
|
|
@ -246,10 +217,10 @@ public class RestTemplateTests {
|
|||
|
||||
@Test
|
||||
public void postForLocationNoLocation() throws Exception {
|
||||
expect(converter.supports(String.class)).andReturn(true).times(2);
|
||||
expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).andReturn(request);
|
||||
String helloWorld = "Hello World";
|
||||
converter.write(helloWorld, request);
|
||||
expect(converter.canWrite(String.class, null)).andReturn(true);
|
||||
converter.write(helloWorld, null, request);
|
||||
expect(request.execute()).andReturn(response);
|
||||
expect(errorHandler.hasError(response)).andReturn(false);
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
|
|
@ -284,21 +255,22 @@ public class RestTemplateTests {
|
|||
|
||||
@Test
|
||||
public void postForObject() throws Exception {
|
||||
expect(converter.supports(String.class)).andReturn(true).times(2);
|
||||
expect(converter.supports(Integer.class)).andReturn(true).times(2);
|
||||
MediaType textPlain = new MediaType("text", "plain");
|
||||
expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(textPlain)).times(2);
|
||||
expect(converter.canRead(Integer.class, null)).andReturn(true);
|
||||
expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(textPlain));
|
||||
expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).andReturn(this.request);
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
expect(this.request.getHeaders()).andReturn(requestHeaders);
|
||||
String request = "Hello World";
|
||||
converter.write(request, this.request);
|
||||
expect(converter.canWrite(String.class, null)).andReturn(true);
|
||||
converter.write(request, null, this.request);
|
||||
expect(this.request.execute()).andReturn(response);
|
||||
expect(errorHandler.hasError(response)).andReturn(false);
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setContentType(textPlain);
|
||||
expect(response.getHeaders()).andReturn(responseHeaders);
|
||||
Integer expected = 42;
|
||||
expect(converter.canRead(Integer.class, textPlain)).andReturn(true);
|
||||
expect(converter.read(Integer.class, response)).andReturn(expected);
|
||||
response.close();
|
||||
|
||||
|
|
@ -313,9 +285,9 @@ public class RestTemplateTests {
|
|||
|
||||
@Test
|
||||
public void postForObjectNull() throws Exception {
|
||||
expect(converter.supports(Integer.class)).andReturn(true).times(2);
|
||||
MediaType textPlain = new MediaType("text", "plain");
|
||||
expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(textPlain)).times(2);
|
||||
expect(converter.canRead(Integer.class, null)).andReturn(true);
|
||||
expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(textPlain));
|
||||
expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.POST)).andReturn(request);
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
expect(request.getHeaders()).andReturn(requestHeaders).times(2);
|
||||
|
|
@ -324,6 +296,7 @@ public class RestTemplateTests {
|
|||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setContentType(textPlain);
|
||||
expect(response.getHeaders()).andReturn(responseHeaders);
|
||||
expect(converter.canRead(Integer.class, textPlain)).andReturn(true);
|
||||
expect(converter.read(Integer.class, response)).andReturn(null);
|
||||
response.close();
|
||||
|
||||
|
|
@ -336,10 +309,10 @@ public class RestTemplateTests {
|
|||
|
||||
@Test
|
||||
public void put() throws Exception {
|
||||
expect(converter.supports(String.class)).andReturn(true).times(2);
|
||||
expect(converter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.PUT)).andReturn(request);
|
||||
String helloWorld = "Hello World";
|
||||
converter.write(helloWorld, request);
|
||||
converter.write(helloWorld, null, request);
|
||||
expect(request.execute()).andReturn(response);
|
||||
expect(errorHandler.hasError(response)).andReturn(false);
|
||||
response.close();
|
||||
|
|
@ -402,7 +375,7 @@ public class RestTemplateTests {
|
|||
|
||||
@Test
|
||||
public void ioException() throws Exception {
|
||||
expect(converter.supports(String.class)).andReturn(true).times(2);
|
||||
expect(converter.canRead(String.class, null)).andReturn(true);
|
||||
MediaType mediaType = new MediaType("foo", "bar");
|
||||
expect(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(mediaType));
|
||||
expect(requestFactory.createRequest(new URI("http://example.com/resource"), HttpMethod.GET)).andReturn(request);
|
||||
|
|
|
|||
Loading…
Reference in New Issue