001/*
002 * Copyright 2002-2017 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.Collections;
020import java.util.List;
021import java.util.Map;
022
023import org.springframework.http.MediaType;
024import org.springframework.util.StringUtils;
025import org.springframework.web.HttpMediaTypeNotAcceptableException;
026import org.springframework.web.context.request.NativeWebRequest;
027
028/**
029 * Base class for {@code ContentNegotiationStrategy} implementations with the
030 * steps to resolve a request to media types.
031 *
032 * <p>First a key (e.g. "json", "pdf") must be extracted from the request (e.g.
033 * file extension, query param). The key must then be resolved to media type(s)
034 * through the base class {@link MappingMediaTypeFileExtensionResolver} which
035 * stores such mappings.
036 *
037 * <p>The method {@link #handleNoMatch} allow sub-classes to plug in additional
038 * ways of looking up media types (e.g. through the Java Activation framework,
039 * or {@link javax.servlet.ServletContext#getMimeType}. Media types resolved
040 * via base classes are then added to the base class
041 * {@link MappingMediaTypeFileExtensionResolver}, i.e. cached for new lookups.
042 *
043 * @author Rossen Stoyanchev
044 * @since 3.2
045 */
046public abstract class AbstractMappingContentNegotiationStrategy extends MappingMediaTypeFileExtensionResolver
047                implements ContentNegotiationStrategy {
048
049        /**
050         * Create an instance with the given map of file extensions and media types.
051         */
052        public AbstractMappingContentNegotiationStrategy(Map<String, MediaType> mediaTypes) {
053                super(mediaTypes);
054        }
055
056
057        @Override
058        public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest)
059                        throws HttpMediaTypeNotAcceptableException {
060
061                return resolveMediaTypeKey(webRequest, getMediaTypeKey(webRequest));
062        }
063
064        /**
065         * An alternative to {@link #resolveMediaTypes(NativeWebRequest)} that accepts
066         * an already extracted key.
067         * @since 3.2.16
068         */
069        public List<MediaType> resolveMediaTypeKey(NativeWebRequest webRequest, String key)
070                        throws HttpMediaTypeNotAcceptableException {
071
072                if (StringUtils.hasText(key)) {
073                        MediaType mediaType = lookupMediaType(key);
074                        if (mediaType != null) {
075                                handleMatch(key, mediaType);
076                                return Collections.singletonList(mediaType);
077                        }
078                        mediaType = handleNoMatch(webRequest, key);
079                        if (mediaType != null) {
080                                addMapping(key, mediaType);
081                                return Collections.singletonList(mediaType);
082                        }
083                }
084                return Collections.emptyList();
085        }
086
087
088        /**
089         * Extract a key from the request to use to look up media types.
090         * @return the lookup key, or {@code null} if none
091         */
092        protected abstract String getMediaTypeKey(NativeWebRequest request);
093
094        /**
095         * Override to provide handling when a key is successfully resolved via
096         * {@link #lookupMediaType}.
097         */
098        protected void handleMatch(String key, MediaType mediaType) {
099        }
100
101        /**
102         * Override to provide handling when a key is not resolved via.
103         * {@link #lookupMediaType}. Sub-classes can take further steps to
104         * determine the media type(s). If a MediaType is returned from
105         * this method it will be added to the cache in the base class.
106         */
107        protected MediaType handleNoMatch(NativeWebRequest request, String key)
108                        throws HttpMediaTypeNotAcceptableException {
109
110                return null;
111        }
112
113}