001/* 002 * Copyright 2012-2018 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 * http://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.boot.autoconfigure.web.servlet; 018 019import java.time.Duration; 020import java.util.LinkedHashMap; 021import java.util.Locale; 022import java.util.Map; 023 024import org.springframework.boot.context.properties.ConfigurationProperties; 025import org.springframework.http.MediaType; 026import org.springframework.util.Assert; 027import org.springframework.validation.DefaultMessageCodesResolver; 028 029/** 030 * {@link ConfigurationProperties properties} for Spring MVC. 031 * 032 * @author Phillip Webb 033 * @author Sébastien Deleuze 034 * @author Stephane Nicoll 035 * @author Eddú Meléndez 036 * @author Brian Clozel 037 * @since 1.1 038 */ 039@ConfigurationProperties(prefix = "spring.mvc") 040public class WebMvcProperties { 041 042 /** 043 * Formatting strategy for message codes. For instance, `PREFIX_ERROR_CODE`. 044 */ 045 private DefaultMessageCodesResolver.Format messageCodesResolverFormat; 046 047 /** 048 * Locale to use. By default, this locale is overridden by the "Accept-Language" 049 * header. 050 */ 051 private Locale locale; 052 053 /** 054 * Define how the locale should be resolved. 055 */ 056 private LocaleResolver localeResolver = LocaleResolver.ACCEPT_HEADER; 057 058 /** 059 * Date format to use. For instance, `dd/MM/yyyy`. 060 */ 061 private String dateFormat; 062 063 /** 064 * Whether to dispatch TRACE requests to the FrameworkServlet doService method. 065 */ 066 private boolean dispatchTraceRequest = false; 067 068 /** 069 * Whether to dispatch OPTIONS requests to the FrameworkServlet doService method. 070 */ 071 private boolean dispatchOptionsRequest = true; 072 073 /** 074 * Whether the content of the "default" model should be ignored during redirect 075 * scenarios. 076 */ 077 private boolean ignoreDefaultModelOnRedirect = true; 078 079 /** 080 * Whether a "NoHandlerFoundException" should be thrown if no Handler was found to 081 * process a request. 082 */ 083 private boolean throwExceptionIfNoHandlerFound = false; 084 085 /** 086 * Whether to enable warn logging of exceptions resolved by a 087 * "HandlerExceptionResolver", except for "DefaultHandlerExceptionResolver". 088 */ 089 private boolean logResolvedException = false; 090 091 /** 092 * Path pattern used for static resources. 093 */ 094 private String staticPathPattern = "/**"; 095 096 private final Async async = new Async(); 097 098 private final Servlet servlet = new Servlet(); 099 100 private final View view = new View(); 101 102 private final Contentnegotiation contentnegotiation = new Contentnegotiation(); 103 104 private final Pathmatch pathmatch = new Pathmatch(); 105 106 public DefaultMessageCodesResolver.Format getMessageCodesResolverFormat() { 107 return this.messageCodesResolverFormat; 108 } 109 110 public void setMessageCodesResolverFormat( 111 DefaultMessageCodesResolver.Format messageCodesResolverFormat) { 112 this.messageCodesResolverFormat = messageCodesResolverFormat; 113 } 114 115 public Locale getLocale() { 116 return this.locale; 117 } 118 119 public void setLocale(Locale locale) { 120 this.locale = locale; 121 } 122 123 public LocaleResolver getLocaleResolver() { 124 return this.localeResolver; 125 } 126 127 public void setLocaleResolver(LocaleResolver localeResolver) { 128 this.localeResolver = localeResolver; 129 } 130 131 public String getDateFormat() { 132 return this.dateFormat; 133 } 134 135 public void setDateFormat(String dateFormat) { 136 this.dateFormat = dateFormat; 137 } 138 139 public boolean isIgnoreDefaultModelOnRedirect() { 140 return this.ignoreDefaultModelOnRedirect; 141 } 142 143 public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) { 144 this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect; 145 } 146 147 public boolean isThrowExceptionIfNoHandlerFound() { 148 return this.throwExceptionIfNoHandlerFound; 149 } 150 151 public void setThrowExceptionIfNoHandlerFound( 152 boolean throwExceptionIfNoHandlerFound) { 153 this.throwExceptionIfNoHandlerFound = throwExceptionIfNoHandlerFound; 154 } 155 156 public boolean isLogResolvedException() { 157 return this.logResolvedException; 158 } 159 160 public void setLogResolvedException(boolean logResolvedException) { 161 this.logResolvedException = logResolvedException; 162 } 163 164 public boolean isDispatchOptionsRequest() { 165 return this.dispatchOptionsRequest; 166 } 167 168 public void setDispatchOptionsRequest(boolean dispatchOptionsRequest) { 169 this.dispatchOptionsRequest = dispatchOptionsRequest; 170 } 171 172 public boolean isDispatchTraceRequest() { 173 return this.dispatchTraceRequest; 174 } 175 176 public void setDispatchTraceRequest(boolean dispatchTraceRequest) { 177 this.dispatchTraceRequest = dispatchTraceRequest; 178 } 179 180 public String getStaticPathPattern() { 181 return this.staticPathPattern; 182 } 183 184 public void setStaticPathPattern(String staticPathPattern) { 185 this.staticPathPattern = staticPathPattern; 186 } 187 188 public Async getAsync() { 189 return this.async; 190 } 191 192 public Servlet getServlet() { 193 return this.servlet; 194 } 195 196 public View getView() { 197 return this.view; 198 } 199 200 public Contentnegotiation getContentnegotiation() { 201 return this.contentnegotiation; 202 } 203 204 public Pathmatch getPathmatch() { 205 return this.pathmatch; 206 } 207 208 public static class Async { 209 210 /** 211 * Amount of time before asynchronous request handling times out. If this value is 212 * not set, the default timeout of the underlying implementation is used, e.g. 10 213 * seconds on Tomcat with Servlet 3. 214 */ 215 private Duration requestTimeout; 216 217 public Duration getRequestTimeout() { 218 return this.requestTimeout; 219 } 220 221 public void setRequestTimeout(Duration requestTimeout) { 222 this.requestTimeout = requestTimeout; 223 } 224 225 } 226 227 public static class Servlet { 228 229 /** 230 * Path of the dispatcher servlet. 231 */ 232 private String path = "/"; 233 234 /** 235 * Load on startup priority of the dispatcher servlet. 236 */ 237 private int loadOnStartup = -1; 238 239 public String getPath() { 240 return this.path; 241 } 242 243 public void setPath(String path) { 244 Assert.notNull(path, "Path must not be null"); 245 Assert.isTrue(!path.contains("*"), "Path must not contain wildcards"); 246 this.path = path; 247 } 248 249 public int getLoadOnStartup() { 250 return this.loadOnStartup; 251 } 252 253 public void setLoadOnStartup(int loadOnStartup) { 254 this.loadOnStartup = loadOnStartup; 255 } 256 257 public String getServletMapping() { 258 if (this.path.equals("") || this.path.equals("/")) { 259 return "/"; 260 } 261 if (this.path.endsWith("/")) { 262 return this.path + "*"; 263 } 264 return this.path + "/*"; 265 } 266 267 public String getPath(String path) { 268 String prefix = getServletPrefix(); 269 if (!path.startsWith("/")) { 270 path = "/" + path; 271 } 272 return prefix + path; 273 } 274 275 public String getServletPrefix() { 276 String result = this.path; 277 int index = result.indexOf('*'); 278 if (index != -1) { 279 result = result.substring(0, index); 280 } 281 if (result.endsWith("/")) { 282 result = result.substring(0, result.length() - 1); 283 } 284 return result; 285 } 286 287 } 288 289 public static class View { 290 291 /** 292 * Spring MVC view prefix. 293 */ 294 private String prefix; 295 296 /** 297 * Spring MVC view suffix. 298 */ 299 private String suffix; 300 301 public String getPrefix() { 302 return this.prefix; 303 } 304 305 public void setPrefix(String prefix) { 306 this.prefix = prefix; 307 } 308 309 public String getSuffix() { 310 return this.suffix; 311 } 312 313 public void setSuffix(String suffix) { 314 this.suffix = suffix; 315 } 316 317 } 318 319 public static class Contentnegotiation { 320 321 /** 322 * Whether the path extension in the URL path should be used to determine the 323 * requested media type. If enabled a request "/users.pdf" will be interpreted as 324 * a request for "application/pdf" regardless of the 'Accept' header. 325 */ 326 private boolean favorPathExtension = false; 327 328 /** 329 * Whether a request parameter ("format" by default) should be used to determine 330 * the requested media type. 331 */ 332 private boolean favorParameter = false; 333 334 /** 335 * Map file extensions to media types for content negotiation. For instance, yml 336 * to text/yaml. 337 */ 338 private Map<String, MediaType> mediaTypes = new LinkedHashMap<>(); 339 340 /** 341 * Query parameter name to use when "favor-parameter" is enabled. 342 */ 343 private String parameterName; 344 345 public boolean isFavorPathExtension() { 346 return this.favorPathExtension; 347 } 348 349 public void setFavorPathExtension(boolean favorPathExtension) { 350 this.favorPathExtension = favorPathExtension; 351 } 352 353 public boolean isFavorParameter() { 354 return this.favorParameter; 355 } 356 357 public void setFavorParameter(boolean favorParameter) { 358 this.favorParameter = favorParameter; 359 } 360 361 public Map<String, MediaType> getMediaTypes() { 362 return this.mediaTypes; 363 } 364 365 public void setMediaTypes(Map<String, MediaType> mediaTypes) { 366 this.mediaTypes = mediaTypes; 367 } 368 369 public String getParameterName() { 370 return this.parameterName; 371 } 372 373 public void setParameterName(String parameterName) { 374 this.parameterName = parameterName; 375 } 376 377 } 378 379 public static class Pathmatch { 380 381 /** 382 * Whether to use suffix pattern match (".*") when matching patterns to requests. 383 * If enabled a method mapped to "/users" also matches to "/users.*". 384 */ 385 private boolean useSuffixPattern = false; 386 387 /** 388 * Whether suffix pattern matching should work only against extensions registered 389 * with "spring.mvc.contentnegotiation.media-types.*". This is generally 390 * recommended to reduce ambiguity and to avoid issues such as when a "." appears 391 * in the path for other reasons. 392 */ 393 private boolean useRegisteredSuffixPattern = false; 394 395 public boolean isUseSuffixPattern() { 396 return this.useSuffixPattern; 397 } 398 399 public void setUseSuffixPattern(boolean useSuffixPattern) { 400 this.useSuffixPattern = useSuffixPattern; 401 } 402 403 public boolean isUseRegisteredSuffixPattern() { 404 return this.useRegisteredSuffixPattern; 405 } 406 407 public void setUseRegisteredSuffixPattern(boolean useRegisteredSuffixPattern) { 408 this.useRegisteredSuffixPattern = useRegisteredSuffixPattern; 409 } 410 411 } 412 413 public enum LocaleResolver { 414 415 /** 416 * Always use the configured locale. 417 */ 418 FIXED, 419 420 /** 421 * Use the "Accept-Language" header or the configured locale if the header is not 422 * set. 423 */ 424 ACCEPT_HEADER 425 426 } 427 428}