001/*
002 * Copyright 2002-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.accept;
018
019import java.util.Locale;
020import java.util.Map;
021
022import javax.servlet.http.HttpServletRequest;
023
024import org.springframework.core.io.Resource;
025import org.springframework.http.MediaType;
026import org.springframework.http.MediaTypeFactory;
027import org.springframework.lang.Nullable;
028import org.springframework.util.Assert;
029import org.springframework.util.StringUtils;
030import org.springframework.web.context.request.NativeWebRequest;
031import org.springframework.web.util.UriUtils;
032import org.springframework.web.util.UrlPathHelper;
033
034/**
035 * A {@code ContentNegotiationStrategy} that resolves the file extension in the
036 * request path to a key to be used to look up a media type.
037 *
038 * <p>If the file extension is not found in the explicit registrations provided
039 * to the constructor, the {@link MediaTypeFactory} is used as a fallback
040 * mechanism.
041 *
042 * @author Rossen Stoyanchev
043 * @since 3.2
044 * @deprecated as of 5.2.4. See class-level note in
045 * {@link ContentNegotiationManagerFactoryBean} on the deprecation of path
046 * extension config options.
047 */
048@Deprecated
049public class PathExtensionContentNegotiationStrategy extends AbstractMappingContentNegotiationStrategy {
050
051        private UrlPathHelper urlPathHelper = new UrlPathHelper();
052
053
054        /**
055         * Create an instance without any mappings to start with. Mappings may be added
056         * later on if any extensions are resolved through the Java Activation framework.
057         */
058        public PathExtensionContentNegotiationStrategy() {
059                this(null);
060        }
061
062        /**
063         * Create an instance with the given map of file extensions and media types.
064         */
065        public PathExtensionContentNegotiationStrategy(@Nullable Map<String, MediaType> mediaTypes) {
066                super(mediaTypes);
067                setUseRegisteredExtensionsOnly(false);
068                setIgnoreUnknownExtensions(true);
069                this.urlPathHelper.setUrlDecode(false);
070        }
071
072
073        /**
074         * Configure a {@code UrlPathHelper} to use in {@link #getMediaTypeKey}
075         * in order to derive the lookup path for a target request URL path.
076         * @since 4.2.8
077         */
078        public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
079                this.urlPathHelper = urlPathHelper;
080        }
081
082        /**
083         * Indicate whether to use the Java Activation Framework as a fallback option
084         * to map from file extensions to media types.
085         * @deprecated as of 5.0, in favor of {@link #setUseRegisteredExtensionsOnly(boolean)}.
086         */
087        @Deprecated
088        public void setUseJaf(boolean useJaf) {
089                setUseRegisteredExtensionsOnly(!useJaf);
090        }
091
092        @Override
093        @Nullable
094        protected String getMediaTypeKey(NativeWebRequest webRequest) {
095                HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
096                if (request == null) {
097                        return null;
098                }
099                // Ignore LOOKUP_PATH attribute, use our own "fixed" UrlPathHelper with decoding off
100                String path = this.urlPathHelper.getLookupPathForRequest(request);
101                String extension = UriUtils.extractFileExtension(path);
102                return (StringUtils.hasText(extension) ? extension.toLowerCase(Locale.ENGLISH) : null);
103        }
104
105        /**
106         * A public method exposing the knowledge of the path extension strategy to
107         * resolve file extensions to a {@link MediaType} in this case for a given
108         * {@link Resource}. The method first looks up any explicitly registered
109         * file extensions first and then falls back on {@link MediaTypeFactory} if available.
110         * @param resource the resource to look up
111         * @return the MediaType for the extension, or {@code null} if none found
112         * @since 4.3
113         */
114        @Nullable
115        public MediaType getMediaTypeForResource(Resource resource) {
116                Assert.notNull(resource, "Resource must not be null");
117                MediaType mediaType = null;
118                String filename = resource.getFilename();
119                String extension = StringUtils.getFilenameExtension(filename);
120                if (extension != null) {
121                        mediaType = lookupMediaType(extension);
122                }
123                if (mediaType == null) {
124                        mediaType = MediaTypeFactory.getMediaType(filename).orElse(null);
125                }
126                return mediaType;
127        }
128
129}