|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2010 the original author or authors. |
|
|
|
* Copyright 2002-2011 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -66,17 +66,17 @@ import org.springframework.web.util.WebUtils; |
|
|
|
* <p>This view resolver uses the requested {@linkplain MediaType media type} to select a suitable {@link View} for a |
|
|
|
* <p>This view resolver uses the requested {@linkplain MediaType media type} to select a suitable {@link View} for a |
|
|
|
* request. This media type is determined by using the following criteria: |
|
|
|
* request. This media type is determined by using the following criteria: |
|
|
|
* <ol> |
|
|
|
* <ol> |
|
|
|
* <li>If the requested path has a file extension and if the {@link #setFavorPathExtension(boolean)} property is |
|
|
|
* <li>If the requested path has a file extension and if the {@link #setFavorPathExtension} property is |
|
|
|
* {@code true}, the {@link #setMediaTypes(Map) mediaTypes} property is inspected for a matching media type.</li> |
|
|
|
* {@code true}, the {@link #setMediaTypes(Map) mediaTypes} property is inspected for a matching media type.</li> |
|
|
|
* <li>If the request contains a parameter defining the extension and if the {@link #setFavorParameter(boolean)} |
|
|
|
* <li>If the request contains a parameter defining the extension and if the {@link #setFavorParameter} |
|
|
|
* property is <code>true</code>, the {@link #setMediaTypes(Map) mediaTypes} property is inspected for a matching |
|
|
|
* property is <code>true</code>, the {@link #setMediaTypes(Map) mediaTypes} property is inspected for a matching |
|
|
|
* media type. The default name of the parameter is <code>format</code> and it can be configured using the |
|
|
|
* media type. The default name of the parameter is <code>format</code> and it can be configured using the |
|
|
|
* {@link #setParameterName(String) parameterName} property.</li> |
|
|
|
* {@link #setParameterName(String) parameterName} property.</li> |
|
|
|
* <li>If there is no match in the {@link #setMediaTypes(Map) mediaTypes} property and if the Java Activation |
|
|
|
* <li>If there is no match in the {@link #setMediaTypes(Map) mediaTypes} property and if the Java Activation |
|
|
|
* Framework (JAF) is both {@linkplain #setUseJaf(boolean) enabled} and present on the class path, |
|
|
|
* Framework (JAF) is both {@linkplain #setUseJaf enabled} and present on the class path, |
|
|
|
* {@link FileTypeMap#getContentType(String)} is used instead.</li> |
|
|
|
* {@link FileTypeMap#getContentType(String)} is used instead.</li> |
|
|
|
* <li>If the previous steps did not result in a media type, and |
|
|
|
* <li>If the previous steps did not result in a media type, and |
|
|
|
* {@link #setIgnoreAcceptHeader(boolean) ignoreAcceptHeader} is {@code false}, the request {@code Accept} header is |
|
|
|
* {@link #setIgnoreAcceptHeader ignoreAcceptHeader} is {@code false}, the request {@code Accept} header is |
|
|
|
* used.</li> |
|
|
|
* used.</li> |
|
|
|
* </ol> |
|
|
|
* </ol> |
|
|
|
* |
|
|
|
* |
|
|
|
@ -145,7 +145,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Indicates whether the extension of the request path should be used to determine the requested media type, |
|
|
|
* Indicate whether the extension of the request path should be used to determine the requested media type, |
|
|
|
* in favor of looking at the {@code Accept} header. The default value is {@code true}. |
|
|
|
* in favor of looking at the {@code Accept} header. The default value is {@code true}. |
|
|
|
* <p>For instance, when this flag is <code>true</code> (the default), a request for {@code /hotels.pdf} |
|
|
|
* <p>For instance, when this flag is <code>true</code> (the default), a request for {@code /hotels.pdf} |
|
|
|
* will result in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the |
|
|
|
* will result in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the |
|
|
|
@ -156,7 +156,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Indicates whether a request parameter should be used to determine the requested media type, |
|
|
|
* Indicate whether a request parameter should be used to determine the requested media type, |
|
|
|
* in favor of looking at the {@code Accept} header. The default value is {@code false}. |
|
|
|
* in favor of looking at the {@code Accept} header. The default value is {@code false}. |
|
|
|
* <p>For instance, when this flag is <code>true</code>, a request for {@code /hotels?format=pdf} will result |
|
|
|
* <p>For instance, when this flag is <code>true</code>, a request for {@code /hotels?format=pdf} will result |
|
|
|
* in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the browser-defined |
|
|
|
* in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the browser-defined |
|
|
|
@ -167,39 +167,38 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Sets the parameter name that can be used to determine the requested media type if the {@link |
|
|
|
* Set the parameter name that can be used to determine the requested media type if the {@link |
|
|
|
* #setFavorParameter(boolean)} property is {@code true}. The default parameter name is {@code format}. |
|
|
|
* #setFavorParameter} property is {@code true}. The default parameter name is {@code format}. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setParameterName(String parameterName) { |
|
|
|
public void setParameterName(String parameterName) { |
|
|
|
this.parameterName = parameterName; |
|
|
|
this.parameterName = parameterName; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Indicates whether the HTTP {@code Accept} header should be ignored. Default is {@code false}. |
|
|
|
* Indicate whether the HTTP {@code Accept} header should be ignored. Default is {@code false}. |
|
|
|
* If set to {@code true}, this view resolver will only refer to the file extension and/or paramter, |
|
|
|
* <p>If set to {@code true}, this view resolver will only refer to the file extension and/or |
|
|
|
* as indicated by the {@link #setFavorPathExtension(boolean) favorPathExtension} and |
|
|
|
* parameter, as indicated by the {@link #setFavorPathExtension favorPathExtension} and |
|
|
|
* {@link #setFavorParameter(boolean) favorParameter} properties. |
|
|
|
* {@link #setFavorParameter favorParameter} properties. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setIgnoreAcceptHeader(boolean ignoreAcceptHeader) { |
|
|
|
public void setIgnoreAcceptHeader(boolean ignoreAcceptHeader) { |
|
|
|
this.ignoreAcceptHeader = ignoreAcceptHeader; |
|
|
|
this.ignoreAcceptHeader = ignoreAcceptHeader; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Indicates whether a {@link HttpServletResponse#SC_NOT_ACCEPTABLE 406 Not Acceptable} status code should be |
|
|
|
* Indicate whether a {@link HttpServletResponse#SC_NOT_ACCEPTABLE 406 Not Acceptable} |
|
|
|
* returned if no suitable view can be found. |
|
|
|
* status code should be returned if no suitable view can be found. |
|
|
|
* |
|
|
|
|
|
|
|
* <p>Default is {@code false}, meaning that this view resolver returns {@code null} for |
|
|
|
* <p>Default is {@code false}, meaning that this view resolver returns {@code null} for |
|
|
|
* {@link #resolveViewName(String, Locale)} when an acceptable view cannot be found. This will allow for view |
|
|
|
* {@link #resolveViewName(String, Locale)} when an acceptable view cannot be found. |
|
|
|
* resolvers chaining. When this property is set to {@code true}, |
|
|
|
* This will allow for view resolvers chaining. When this property is set to {@code true}, |
|
|
|
* {@link #resolveViewName(String, Locale)} will respond with a view that sets the response status to |
|
|
|
* {@link #resolveViewName(String, Locale)} will respond with a view that sets the |
|
|
|
* {@code 406 Not Acceptable} instead. |
|
|
|
* response status to {@code 406 Not Acceptable} instead. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setUseNotAcceptableStatusCode(boolean useNotAcceptableStatusCode) { |
|
|
|
public void setUseNotAcceptableStatusCode(boolean useNotAcceptableStatusCode) { |
|
|
|
this.useNotAcceptableStatusCode = useNotAcceptableStatusCode; |
|
|
|
this.useNotAcceptableStatusCode = useNotAcceptableStatusCode; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Sets the mapping from file extensions to media types. |
|
|
|
* Set the mapping from file extensions to media types. |
|
|
|
* <p>When this mapping is not set or when an extension is not present, this view resolver |
|
|
|
* <p>When this mapping is not set or when an extension is not present, this view resolver |
|
|
|
* will fall back to using a {@link FileTypeMap} when the Java Action Framework is available. |
|
|
|
* will fall back to using a {@link FileTypeMap} when the Java Action Framework is available. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@ -213,7 +212,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Sets the default views to use when a more specific view can not be obtained |
|
|
|
* Set the default views to use when a more specific view can not be obtained |
|
|
|
* from the {@link ViewResolver} chain. |
|
|
|
* from the {@link ViewResolver} chain. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setDefaultViews(List<View> defaultViews) { |
|
|
|
public void setDefaultViews(List<View> defaultViews) { |
|
|
|
@ -221,7 +220,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Sets the default content type. |
|
|
|
* Set the default content type. |
|
|
|
* <p>This content type will be used when file extension, parameter, nor {@code Accept} |
|
|
|
* <p>This content type will be used when file extension, parameter, nor {@code Accept} |
|
|
|
* header define a content-type, either through being disabled or empty. |
|
|
|
* header define a content-type, either through being disabled or empty. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@ -230,7 +229,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Indicates whether to use the Java Activation Framework to map from file extensions to media types. |
|
|
|
* Indicate whether to use the Java Activation Framework to map from file extensions to media types. |
|
|
|
* <p>Default is {@code true}, i.e. the Java Activation Framework is used (if available). |
|
|
|
* <p>Default is {@code true}, i.e. the Java Activation Framework is used (if available). |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setUseJaf(boolean useJaf) { |
|
|
|
public void setUseJaf(boolean useJaf) { |
|
|
|
@ -265,13 +264,38 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
OrderComparator.sort(this.viewResolvers); |
|
|
|
OrderComparator.sort(this.viewResolvers); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public View resolveViewName(String viewName, Locale locale) throws Exception { |
|
|
|
|
|
|
|
RequestAttributes attrs = RequestContextHolder.getRequestAttributes(); |
|
|
|
|
|
|
|
Assert.isInstanceOf(ServletRequestAttributes.class, attrs); |
|
|
|
|
|
|
|
List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest()); |
|
|
|
|
|
|
|
if (requestedMediaTypes != null) { |
|
|
|
|
|
|
|
List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes); |
|
|
|
|
|
|
|
View bestView = getBestView(candidateViews, requestedMediaTypes); |
|
|
|
|
|
|
|
if (bestView != null) { |
|
|
|
|
|
|
|
return bestView; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (this.useNotAcceptableStatusCode) { |
|
|
|
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
|
|
|
logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return NOT_ACCEPTABLE_VIEW; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
|
|
|
logger.debug("No acceptable view found; returning null"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Determines the list of {@link MediaType} for the given {@link HttpServletRequest}. |
|
|
|
* Determines the list of {@link MediaType} for the given {@link HttpServletRequest}. |
|
|
|
* <p>The default implementation invokes {@link #getMediaTypeFromFilename(String)} if {@linkplain |
|
|
|
* <p>The default implementation invokes {@link #getMediaTypeFromFilename(String)} if {@linkplain |
|
|
|
* #setFavorPathExtension(boolean) favorPathExtension} property is <code>true</code>. If the property is |
|
|
|
* #setFavorPathExtension favorPathExtension} property is <code>true</code>. If the property is |
|
|
|
* <code>false</code>, or when a media type cannot be determined from the request path, this method will |
|
|
|
* <code>false</code>, or when a media type cannot be determined from the request path, |
|
|
|
* inspect the {@code Accept} header of the request. |
|
|
|
* this method will inspect the {@code Accept} header of the request. |
|
|
|
* <p>This method can be overriden to provide a different algorithm. |
|
|
|
* <p>This method can be overridden to provide a different algorithm. |
|
|
|
* @param request the current servlet request |
|
|
|
* @param request the current servlet request |
|
|
|
* @return the list of media types requested, if any |
|
|
|
* @return the list of media types requested, if any |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@ -303,6 +327,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
if (!this.ignoreAcceptHeader) { |
|
|
|
if (!this.ignoreAcceptHeader) { |
|
|
|
String acceptHeader = request.getHeader(ACCEPT_HEADER); |
|
|
|
String acceptHeader = request.getHeader(ACCEPT_HEADER); |
|
|
|
if (StringUtils.hasText(acceptHeader)) { |
|
|
|
if (StringUtils.hasText(acceptHeader)) { |
|
|
|
|
|
|
|
try { |
|
|
|
List<MediaType> mediaTypes = MediaType.parseMediaTypes(acceptHeader); |
|
|
|
List<MediaType> mediaTypes = MediaType.parseMediaTypes(acceptHeader); |
|
|
|
MediaType.sortByQualityValue(mediaTypes); |
|
|
|
MediaType.sortByQualityValue(mediaTypes); |
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
@ -310,6 +335,13 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
} |
|
|
|
} |
|
|
|
return mediaTypes; |
|
|
|
return mediaTypes; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
catch (IllegalArgumentException ex) { |
|
|
|
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
|
|
|
logger.debug("Could not parse accept header [" + acceptHeader + "]: " + ex.getMessage()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (this.defaultContentType != null) { |
|
|
|
if (this.defaultContentType != null) { |
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
@ -360,31 +392,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
return this.mediaTypes.get(parameterValue.toLowerCase(Locale.ENGLISH)); |
|
|
|
return this.mediaTypes.get(parameterValue.toLowerCase(Locale.ENGLISH)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public View resolveViewName(String viewName, Locale locale) throws Exception { |
|
|
|
|
|
|
|
RequestAttributes attrs = RequestContextHolder.getRequestAttributes(); |
|
|
|
|
|
|
|
Assert.isInstanceOf(ServletRequestAttributes.class, attrs); |
|
|
|
|
|
|
|
List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest()); |
|
|
|
|
|
|
|
List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes); |
|
|
|
|
|
|
|
View bestView = getBestView(candidateViews, requestedMediaTypes); |
|
|
|
|
|
|
|
if (bestView != null) { |
|
|
|
|
|
|
|
return bestView; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
if (this.useNotAcceptableStatusCode) { |
|
|
|
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
|
|
|
logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return NOT_ACCEPTABLE_VIEW; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
|
|
|
logger.debug("No acceptable view found; returning null"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) |
|
|
|
private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) |
|
|
|
throws Exception { |
|
|
|
throws Exception { |
|
|
|
|
|
|
|
|
|
|
|
@ -414,7 +421,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
|
|
|
|
|
|
|
|
private List<String> getExtensionsForMediaType(MediaType requestedMediaType) { |
|
|
|
private List<String> getExtensionsForMediaType(MediaType requestedMediaType) { |
|
|
|
List<String> result = new ArrayList<String>(); |
|
|
|
List<String> result = new ArrayList<String>(); |
|
|
|
for (Entry<String, MediaType> entry : mediaTypes.entrySet()) { |
|
|
|
for (Entry<String, MediaType> entry : this.mediaTypes.entrySet()) { |
|
|
|
if (requestedMediaType.includes(entry.getValue())) { |
|
|
|
if (requestedMediaType.includes(entry.getValue())) { |
|
|
|
result.add(entry.getKey()); |
|
|
|
result.add(entry.getKey()); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -438,9 +445,8 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
} |
|
|
|
} |
|
|
|
if (bestView != null) { |
|
|
|
if (bestView != null) { |
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
logger.debug( |
|
|
|
logger.debug("Returning [" + bestView + "] based on requested media type '" + |
|
|
|
"Returning [" + bestView + "] based on requested media type '" + bestRequestedMediaType + |
|
|
|
bestRequestedMediaType + "'"); |
|
|
|
"'"); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -495,7 +501,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
|
|
|
|
|
|
|
|
public static MediaType getMediaType(String fileName) { |
|
|
|
public static MediaType getMediaType(String fileName) { |
|
|
|
String mediaType = fileTypeMap.getContentType(fileName); |
|
|
|
String mediaType = fileTypeMap.getContentType(fileName); |
|
|
|
return StringUtils.hasText(mediaType) ? MediaType.parseMediaType(mediaType) : null; |
|
|
|
return (StringUtils.hasText(mediaType) ? MediaType.parseMediaType(mediaType) : null); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -506,8 +512,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) |
|
|
|
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) { |
|
|
|
throws Exception { |
|
|
|
|
|
|
|
response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); |
|
|
|
response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|