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.servlet.config.annotation; 018 019import java.util.HashMap; 020import java.util.Map; 021import javax.servlet.ServletContext; 022 023import org.springframework.http.MediaType; 024import org.springframework.web.accept.ContentNegotiationManager; 025import org.springframework.web.accept.ContentNegotiationManagerFactoryBean; 026import org.springframework.web.accept.ContentNegotiationStrategy; 027import org.springframework.web.accept.FixedContentNegotiationStrategy; 028import org.springframework.web.accept.HeaderContentNegotiationStrategy; 029import org.springframework.web.accept.ParameterContentNegotiationStrategy; 030import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; 031 032/** 033 * Creates a {@code ContentNegotiationManager} and configures it with 034 * one or more {@link ContentNegotiationStrategy} instances. 035 * 036 * <p>As an alternative you can also rely on the set of defaults described below 037 * which can be turned on or off or customized through the methods of this 038 * builder: 039 * 040 * <table> 041 * <tr> 042 * <th>Configurer Property</th> 043 * <th>Underlying Strategy</th> 044 * <th>Default Setting</th> 045 * </tr> 046 * <tr> 047 * <td>{@link #favorPathExtension}</td> 048 * <td>{@link PathExtensionContentNegotiationStrategy Path Extension strategy}</td> 049 * <td>On</td> 050 * </tr> 051 * <tr> 052 * <td>{@link #favorParameter}</td> 053 * <td>{@link ParameterContentNegotiationStrategy Parameter strategy}</td> 054 * <td>Off</td> 055 * </tr> 056 * <tr> 057 * <td>{@link #ignoreAcceptHeader}</td> 058 * <td>{@link HeaderContentNegotiationStrategy Header strategy}</td> 059 * <td>On</td> 060 * </tr> 061 * <tr> 062 * <td>{@link #defaultContentType}</td> 063 * <td>{@link FixedContentNegotiationStrategy Fixed content strategy}</td> 064 * <td>Not set</td> 065 * </tr> 066 * <tr> 067 * <td>{@link #defaultContentTypeStrategy}</td> 068 * <td>{@link ContentNegotiationStrategy}</td> 069 * <td>Not set</td> 070 * </tr> 071 * </table> 072 * 073 * <p>The order in which strategies are configured is fixed. You can only turn 074 * them on or off. 075 * 076 * <p>For the path extension and parameter strategies you may explicitly add 077 * {@link #mediaType MediaType mappings}. Those will be used to resolve path 078 * extensions and/or a query parameter value such as "json" to a concrete media 079 * type such as "application/json". 080 * 081 * <p>The path extension strategy will also use {@link ServletContext#getMimeType} 082 * and the Java Activation framework (JAF), if available, to resolve a path 083 * extension to a MediaType. You may however {@link #useJaf suppress} the use 084 * of JAF. 085 * 086 * @author Rossen Stoyanchev 087 * @author Brian Clozel 088 * @author Juergen Hoeller 089 * @since 3.2 090 * @see ContentNegotiationManagerFactoryBean 091 */ 092public class ContentNegotiationConfigurer { 093 094 private final ContentNegotiationManagerFactoryBean factory = new ContentNegotiationManagerFactoryBean(); 095 096 private final Map<String, MediaType> mediaTypes = new HashMap<String, MediaType>(); 097 098 099 /** 100 * Class constructor with {@link javax.servlet.ServletContext}. 101 */ 102 public ContentNegotiationConfigurer(ServletContext servletContext) { 103 this.factory.setServletContext(servletContext); 104 } 105 106 107 /** 108 * Whether the path extension in the URL path should be used to determine 109 * the requested media type. 110 * <p>By default this is set to {@code true} in which case a request 111 * for {@code /hotels.pdf} will be interpreted as a request for 112 * {@code "application/pdf"} regardless of the 'Accept' header. 113 */ 114 public ContentNegotiationConfigurer favorPathExtension(boolean favorPathExtension) { 115 this.factory.setFavorPathExtension(favorPathExtension); 116 return this; 117 } 118 119 /** 120 * Add a mapping from a key, extracted from a path extension or a query 121 * parameter, to a MediaType. This is required in order for the parameter 122 * strategy to work. Any extensions explicitly registered here are also 123 * whitelisted for the purpose of Reflected File Download attack detection 124 * (see Spring Framework reference documentation for more details on RFD 125 * attack protection). 126 * <p>The path extension strategy will also try to use 127 * {@link ServletContext#getMimeType} and JAF (if present) to resolve path 128 * extensions. To change this behavior see the {@link #useJaf} property. 129 * @param extension the key to look up 130 * @param mediaType the media type 131 * @see #mediaTypes(Map) 132 * @see #replaceMediaTypes(Map) 133 */ 134 public ContentNegotiationConfigurer mediaType(String extension, MediaType mediaType) { 135 this.mediaTypes.put(extension, mediaType); 136 return this; 137 } 138 139 /** 140 * An alternative to {@link #mediaType}. 141 * @see #mediaType(String, MediaType) 142 * @see #replaceMediaTypes(Map) 143 */ 144 public ContentNegotiationConfigurer mediaTypes(Map<String, MediaType> mediaTypes) { 145 if (mediaTypes != null) { 146 this.mediaTypes.putAll(mediaTypes); 147 } 148 return this; 149 } 150 151 /** 152 * Similar to {@link #mediaType} but for replacing existing mappings. 153 * @see #mediaType(String, MediaType) 154 * @see #mediaTypes(Map) 155 */ 156 public ContentNegotiationConfigurer replaceMediaTypes(Map<String, MediaType> mediaTypes) { 157 this.mediaTypes.clear(); 158 mediaTypes(mediaTypes); 159 return this; 160 } 161 162 /** 163 * Whether to ignore requests with path extension that cannot be resolved 164 * to any media type. Setting this to {@code false} will result in an 165 * {@code HttpMediaTypeNotAcceptableException} if there is no match. 166 * <p>By default this is set to {@code true}. 167 */ 168 public ContentNegotiationConfigurer ignoreUnknownPathExtensions(boolean ignore) { 169 this.factory.setIgnoreUnknownPathExtensions(ignore); 170 return this; 171 } 172 173 /** 174 * When {@link #favorPathExtension} is set, this property determines whether 175 * to allow use of JAF (Java Activation Framework) to resolve a path 176 * extension to a specific MediaType. 177 * <p>By default this is not set in which case 178 * {@code PathExtensionContentNegotiationStrategy} will use JAF if available. 179 */ 180 public ContentNegotiationConfigurer useJaf(boolean useJaf) { 181 this.factory.setUseJaf(useJaf); 182 return this; 183 } 184 185 /** 186 * Whether a request parameter ("format" by default) should be used to 187 * determine the requested media type. For this option to work you must 188 * register {@link #mediaType(String, MediaType) media type mappings}. 189 * <p>By default this is set to {@code false}. 190 * @see #parameterName(String) 191 */ 192 public ContentNegotiationConfigurer favorParameter(boolean favorParameter) { 193 this.factory.setFavorParameter(favorParameter); 194 return this; 195 } 196 197 /** 198 * Set the query parameter name to use when {@link #favorParameter} is on. 199 * <p>The default parameter name is {@code "format"}. 200 */ 201 public ContentNegotiationConfigurer parameterName(String parameterName) { 202 this.factory.setParameterName(parameterName); 203 return this; 204 } 205 206 /** 207 * Whether to disable checking the 'Accept' request header. 208 * <p>By default this value is set to {@code false}. 209 */ 210 public ContentNegotiationConfigurer ignoreAcceptHeader(boolean ignoreAcceptHeader) { 211 this.factory.setIgnoreAcceptHeader(ignoreAcceptHeader); 212 return this; 213 } 214 215 /** 216 * Set the default content type to use when no content type is requested. 217 * <p>By default this is not set. 218 * @see #defaultContentTypeStrategy 219 */ 220 public ContentNegotiationConfigurer defaultContentType(MediaType defaultContentType) { 221 this.factory.setDefaultContentType(defaultContentType); 222 return this; 223 } 224 225 /** 226 * Set a custom {@link ContentNegotiationStrategy} to use to determine 227 * the content type to use when no content type is requested. 228 * <p>By default this is not set. 229 * @see #defaultContentType 230 * @since 4.1.2 231 */ 232 public ContentNegotiationConfigurer defaultContentTypeStrategy(ContentNegotiationStrategy defaultStrategy) { 233 this.factory.setDefaultContentTypeStrategy(defaultStrategy); 234 return this; 235 } 236 237 238 /** 239 * Build a {@link ContentNegotiationManager} based on this configurer's settings. 240 * @since 4.3.12 241 * @see ContentNegotiationManagerFactoryBean#getObject() 242 */ 243 protected ContentNegotiationManager buildContentNegotiationManager() { 244 this.factory.addMediaTypes(this.mediaTypes); 245 this.factory.afterPropertiesSet(); 246 return this.factory.getObject(); 247 } 248 249 /** 250 * @deprecated as of 4.3.12, in favor of {@link #buildContentNegotiationManager()} 251 */ 252 @Deprecated 253 protected ContentNegotiationManager getContentNegotiationManager() throws Exception { 254 return buildContentNegotiationManager(); 255 } 256 257}