ResourceHttpRequestHandler sends content without content-type header if no media type found (SPR-7713); ResourceHttpRequestHandler and ContentNegotiatingViewResolver use consistent mime type resolution
This commit is contained in:
@@ -17,14 +17,19 @@
|
||||
package org.springframework.web.servlet.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.activation.MimetypesFileTypeMap;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.HttpRequestHandler;
|
||||
@@ -63,6 +68,9 @@ import org.springframework.web.servlet.support.WebContentGenerator;
|
||||
*/
|
||||
public class ResourceHttpRequestHandler extends WebContentGenerator implements HttpRequestHandler {
|
||||
|
||||
private static final boolean jafPresent =
|
||||
ClassUtils.isPresent("javax.activation.FileTypeMap", ResourceHttpRequestHandler.class.getClassLoader());
|
||||
|
||||
private List<Resource> locations;
|
||||
|
||||
|
||||
@@ -109,15 +117,13 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
MediaType mediaType = getMediaType(resource);
|
||||
if (mediaType != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Determined media type [" + mediaType + "] for " + resource);
|
||||
logger.debug("Determined media type '" + mediaType + "' for " + resource);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No media type found for " + resource + " - returning 404");
|
||||
logger.debug("No media type found for " + resource + " - not sending a content-type header");
|
||||
}
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// header phase
|
||||
@@ -190,7 +196,15 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
*/
|
||||
protected MediaType getMediaType(Resource resource) {
|
||||
String mimeType = getServletContext().getMimeType(resource.getFilename());
|
||||
return (StringUtils.hasText(mimeType) ? MediaType.parseMediaType(mimeType) : null);
|
||||
if (StringUtils.hasText(mimeType)) {
|
||||
return new MediaType(mimeType);
|
||||
}
|
||||
else if (jafPresent) {
|
||||
return ActivationMediaTypeFactory.getMediaType(resource.getFilename());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,7 +221,10 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
throw new IOException("Resource content too long (beyond Integer.MAX_VALUE): " + resource);
|
||||
}
|
||||
response.setContentLength((int) length);
|
||||
response.setContentType(mediaType.toString());
|
||||
|
||||
if (mediaType != null) {
|
||||
response.setContentType(mediaType.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,4 +238,48 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
FileCopyUtils.copy(resource.getInputStream(), response.getOutputStream());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid hard-coded JAF dependency.
|
||||
*/
|
||||
private static class ActivationMediaTypeFactory {
|
||||
|
||||
private static final FileTypeMap fileTypeMap;
|
||||
|
||||
static {
|
||||
fileTypeMap = loadFileTypeMapFromContextSupportModule();
|
||||
}
|
||||
|
||||
private static FileTypeMap loadFileTypeMapFromContextSupportModule() {
|
||||
// see if we can find the extended mime.types from the context-support module
|
||||
Resource mappingLocation = new ClassPathResource("org/springframework/mail/javamail/mime.types");
|
||||
if (mappingLocation.exists()) {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = mappingLocation.getInputStream();
|
||||
return new MimetypesFileTypeMap(inputStream);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return FileTypeMap.getDefaultFileTypeMap();
|
||||
}
|
||||
|
||||
public static MediaType getMediaType(String filename) {
|
||||
String mediaType = fileTypeMap.getContentType(filename);
|
||||
return (StringUtils.hasText(mediaType) ? MediaType.parseMediaType(mediaType) : null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -78,14 +78,14 @@ import org.springframework.web.util.WebUtils;
|
||||
* 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>
|
||||
* <li>If there is no match in the {@link #setMediaTypes(Map) mediaTypes} property and if the Java Activation
|
||||
* Framework (JAF) is both {@linkplain #setUseJaf enabled} and present on the class path,
|
||||
* Framework (JAF) is both {@linkplain #setUseJaf enabled} and present on the classpath,
|
||||
* {@link FileTypeMap#getContentType(String)} is used instead.</li>
|
||||
* <li>If the previous steps did not result in a media type, and
|
||||
* {@link #setIgnoreAcceptHeader ignoreAcceptHeader} is {@code false}, the request {@code Accept} header is
|
||||
* used.</li>
|
||||
* </ol>
|
||||
*
|
||||
* Once the requested media type has been determined, this resolver queries each delegate view resolver for a
|
||||
* <p>Once the requested media type has been determined, this resolver queries each delegate view resolver for a
|
||||
* {@link View} and determines if the requested media type is {@linkplain MediaType#includes(MediaType) compatible}
|
||||
* with the view's {@linkplain View#getContentType() content type}). The most compatible view is returned.
|
||||
*
|
||||
@@ -407,7 +407,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
* <p>The default implementation will check the {@linkplain #setMediaTypes(Map) media types}
|
||||
* property first for a defined mapping. If not present, and if the Java Activation Framework
|
||||
* can be found on the classpath, it will call {@link FileTypeMap#getContentType(String)}
|
||||
* <p>This method can be overriden to provide a different algorithm.
|
||||
* <p>This method can be overridden to provide a different algorithm.
|
||||
* @param filename the current request file name (i.e. {@code hotels.html})
|
||||
* @return the media type, if any
|
||||
*/
|
||||
@@ -418,8 +418,14 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
}
|
||||
extension = extension.toLowerCase(Locale.ENGLISH);
|
||||
MediaType mediaType = this.mediaTypes.get(extension);
|
||||
if (mediaType == null && this.useJaf && jafPresent) {
|
||||
mediaType = ActivationMediaTypeFactory.getMediaType(filename);
|
||||
if (mediaType == null) {
|
||||
String mimeType = getServletContext().getMimeType(filename);
|
||||
if (StringUtils.hasText(mimeType)) {
|
||||
mediaType = new MediaType(mimeType);
|
||||
}
|
||||
else if (this.useJaf && jafPresent) {
|
||||
mediaType = ActivationMediaTypeFactory.getMediaType(filename);
|
||||
}
|
||||
if (mediaType != null) {
|
||||
this.mediaTypes.putIfAbsent(extension, mediaType);
|
||||
}
|
||||
@@ -506,6 +512,18 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
}
|
||||
|
||||
|
||||
private static final View NOT_ACCEPTABLE_VIEW = new View() {
|
||||
|
||||
public String getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid hard-coded JAF dependency.
|
||||
*/
|
||||
@@ -549,22 +567,10 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
return FileTypeMap.getDefaultFileTypeMap();
|
||||
}
|
||||
|
||||
public static MediaType getMediaType(String fileName) {
|
||||
String mediaType = fileTypeMap.getContentType(fileName);
|
||||
public static MediaType getMediaType(String filename) {
|
||||
String mediaType = fileTypeMap.getContentType(filename);
|
||||
return (StringUtils.hasText(mediaType) ? MediaType.parseMediaType(mediaType) : null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final View NOT_ACCEPTABLE_VIEW = new View() {
|
||||
|
||||
public String getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user