Browse Source

ResourceHttpRequestHandler sends content without content-type header if no media type found (SPR-7713); ResourceHttpRequestHandler and ContentNegotiatingViewResolver use consistent mime type resolution

pull/7/head
Juergen Hoeller 14 years ago
parent
commit
34a4fba335
  1. 73
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java
  2. 44
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.java

73
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java

@ -17,14 +17,19 @@ @@ -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; @@ -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 @@ -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 @@ -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 @@ -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 @@ -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);
}
}
}

44
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.java

@ -78,14 +78,14 @@ import org.springframework.web.util.WebUtils; @@ -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 @@ -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 @@ -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 @@ -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 @@ -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);
}
};
}

Loading…
Cancel
Save