org.springframework.web.servlet.support.RequestContext Maven / Gradle / Ivy
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.support;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.jsp.jstl.core.Config;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.SimpleTimeZoneAwareLocaleContext;
import org.springframework.context.i18n.TimeZoneAwareLocaleContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.bind.EscapedErrors;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.LocaleContextResolver;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.util.HtmlUtils;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.WebUtils;
/**
* Context holder for request-specific state, like current web application context, current locale,
* current theme, and potential binding errors. Provides easy access to localized messages and
* Errors instances.
*
* Suitable for exposition to views, and usage within JSP's "useBean" tag, JSP scriptlets, JSTL EL,
* etc. Necessary for views that do not have access to the servlet request, like FreeMarker templates.
*
*
Can be instantiated manually, or automatically exposed to views as model attribute via AbstractView's
* "requestContextAttribute" property.
*
*
Will also work outside DispatcherServlet requests, accessing the root WebApplicationContext
* and using an appropriate fallback for the locale (the HttpServletRequest's primary locale).
*
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 03.03.2003
* @see org.springframework.web.servlet.DispatcherServlet
* @see org.springframework.web.servlet.view.AbstractView#setRequestContextAttribute
* @see org.springframework.web.servlet.view.UrlBasedViewResolver#setRequestContextAttribute
*/
public class RequestContext {
/**
* Default theme name used if the RequestContext cannot find a ThemeResolver.
* Only applies to non-DispatcherServlet requests.
*
Same as AbstractThemeResolver's default, but not linked in here to avoid package interdependencies.
* @see org.springframework.web.servlet.theme.AbstractThemeResolver#ORIGINAL_DEFAULT_THEME_NAME
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated(since = "6.0")
public static final String DEFAULT_THEME_NAME = "theme";
/**
* Request attribute to hold the current web application context for RequestContext usage.
* By default, the DispatcherServlet's context (or the root context as fallback) is exposed.
*/
public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = RequestContext.class.getName() + ".CONTEXT";
protected static final boolean jstlPresent = ClassUtils.isPresent(
"jakarta.servlet.jsp.jstl.core.Config", RequestContext.class.getClassLoader());
private final HttpServletRequest request;
@Nullable
private final HttpServletResponse response;
@Nullable
private final Map model;
private final WebApplicationContext webApplicationContext;
@Nullable
private Locale locale;
@Nullable
private TimeZone timeZone;
@Deprecated
@Nullable
private org.springframework.ui.context.Theme theme;
@Nullable
private Boolean defaultHtmlEscape;
@Nullable
private final Boolean responseEncodedHtmlEscape;
private UrlPathHelper urlPathHelper;
@Nullable
private RequestDataValueProcessor requestDataValueProcessor;
@Nullable
private Map errorsMap;
/**
* Create a new RequestContext for the given request, using the request attributes for Errors retrieval.
* This only works with InternalResourceViews, as Errors instances are part of the model and not
* normally exposed as request attributes. It will typically be used within JSPs or custom tags.
*
Will only work within a DispatcherServlet request.
* Pass in a ServletContext to be able to fall back to the root WebApplicationContext.
* @param request current HTTP request
* @see org.springframework.web.servlet.DispatcherServlet
* @see #RequestContext(jakarta.servlet.http.HttpServletRequest, jakarta.servlet.ServletContext)
*/
public RequestContext(HttpServletRequest request) {
this(request, null, null, null);
}
/**
* Create a new RequestContext for the given request, using the request attributes for Errors retrieval.
*
This only works with InternalResourceViews, as Errors instances are part of the model and not
* normally exposed as request attributes. It will typically be used within JSPs or custom tags.
*
Will only work within a DispatcherServlet request.
* Pass in a ServletContext to be able to fall back to the root WebApplicationContext.
* @param request current HTTP request
* @param response current HTTP response
* @see org.springframework.web.servlet.DispatcherServlet
* @see #RequestContext(jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse, jakarta.servlet.ServletContext, Map)
*/
public RequestContext(HttpServletRequest request, HttpServletResponse response) {
this(request, response, null, null);
}
/**
* Create a new RequestContext for the given request, using the request attributes for Errors retrieval.
*
This only works with InternalResourceViews, as Errors instances are part of the model and not
* normally exposed as request attributes. It will typically be used within JSPs or custom tags.
*
If a ServletContext is specified, the RequestContext will also work with the root
* WebApplicationContext (outside a DispatcherServlet).
* @param request current HTTP request
* @param servletContext the servlet context of the web application (can be {@code null};
* necessary for fallback to root WebApplicationContext)
* @see org.springframework.web.context.WebApplicationContext
* @see org.springframework.web.servlet.DispatcherServlet
*/
public RequestContext(HttpServletRequest request, @Nullable ServletContext servletContext) {
this(request, null, servletContext, null);
}
/**
* Create a new RequestContext for the given request, using the given model attributes for Errors retrieval.
*
This works with all View implementations. It will typically be used by View implementations.
*
Will only work within a DispatcherServlet request.
* Pass in a ServletContext to be able to fall back to the root WebApplicationContext.
* @param request current HTTP request
* @param model the model attributes for the current view (can be {@code null},
* using the request attributes for Errors retrieval)
* @see org.springframework.web.servlet.DispatcherServlet
* @see #RequestContext(jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse, jakarta.servlet.ServletContext, Map)
*/
public RequestContext(HttpServletRequest request, @Nullable Map model) {
this(request, null, null, model);
}
/**
* Create a new RequestContext for the given request, using the given model attributes for Errors retrieval.
* This works with all View implementations. It will typically be used by View implementations.
*
If a ServletContext is specified, the RequestContext will also work with a root
* WebApplicationContext (outside a DispatcherServlet).
* @param request current HTTP request
* @param response current HTTP response
* @param servletContext the servlet context of the web application (can be {@code null}; necessary for
* fallback to root WebApplicationContext)
* @param model the model attributes for the current view (can be {@code null}, using the request attributes
* for Errors retrieval)
* @see org.springframework.web.context.WebApplicationContext
* @see org.springframework.web.servlet.DispatcherServlet
*/
public RequestContext(HttpServletRequest request, @Nullable HttpServletResponse response,
@Nullable ServletContext servletContext, @Nullable Map model) {
this.request = request;
this.response = response;
this.model = model;
// Fetch WebApplicationContext, either from DispatcherServlet or the root context.
// ServletContext needs to be specified to be able to fall back to the root context!
WebApplicationContext wac = (WebApplicationContext) request.getAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE);
if (wac == null) {
wac = RequestContextUtils.findWebApplicationContext(request, servletContext);
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: not in a DispatcherServlet " +
"request and no ContextLoaderListener registered?");
}
}
this.webApplicationContext = wac;
Locale locale = null;
TimeZone timeZone = null;
// Determine locale to use for this RequestContext.
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver instanceof LocaleContextResolver localeContextResolver) {
LocaleContext localeContext = localeContextResolver.resolveLocaleContext(request);
locale = localeContext.getLocale();
if (localeContext instanceof TimeZoneAwareLocaleContext timeZoneAwareLocaleContext) {
timeZone = timeZoneAwareLocaleContext.getTimeZone();
}
}
else if (localeResolver != null) {
// Try LocaleResolver (we're within a DispatcherServlet request).
locale = localeResolver.resolveLocale(request);
}
this.locale = locale;
this.timeZone = timeZone;
// Determine default HTML escape setting from the "defaultHtmlEscape"
// context-param in web.xml, if any.
this.defaultHtmlEscape = WebUtils.getDefaultHtmlEscape(this.webApplicationContext.getServletContext());
// Determine response-encoded HTML escape setting from the "responseEncodedHtmlEscape"
// context-param in web.xml, if any.
this.responseEncodedHtmlEscape =
WebUtils.getResponseEncodedHtmlEscape(this.webApplicationContext.getServletContext());
this.urlPathHelper = new UrlPathHelper();
if (this.webApplicationContext.containsBean(RequestContextUtils.REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME)) {
this.requestDataValueProcessor = this.webApplicationContext.getBean(
RequestContextUtils.REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME, RequestDataValueProcessor.class);
}
}
/**
* Return the underlying HttpServletRequest. Only intended for cooperating classes in this package.
*/
protected final HttpServletRequest getRequest() {
return this.request;
}
/**
* Return the underlying ServletContext. Only intended for cooperating classes in this package.
*/
@Nullable
protected final ServletContext getServletContext() {
return this.webApplicationContext.getServletContext();
}
/**
* Return the current WebApplicationContext.
*/
public final WebApplicationContext getWebApplicationContext() {
return this.webApplicationContext;
}
/**
* Return the current WebApplicationContext as MessageSource.
*/
public final MessageSource getMessageSource() {
return this.webApplicationContext;
}
/**
* Return the model Map that this RequestContext encapsulates, if any.
* @return the populated model Map, or {@code null} if none available
*/
@Nullable
public final Map getModel() {
return this.model;
}
/**
* Return the current Locale (falling back to the request locale; never {@code null}).
* Typically coming from a DispatcherServlet's {@link LocaleResolver}.
* Also includes a fallback check for JSTL's Locale attribute.
* @see RequestContextUtils#getLocale
*/
public final Locale getLocale() {
return (this.locale != null ? this.locale : getFallbackLocale());
}
/**
* Return the current TimeZone (or {@code null} if none derivable from the request).
*
Typically coming from a DispatcherServlet's {@link LocaleContextResolver}.
* Also includes a fallback check for JSTL's TimeZone attribute.
* @see RequestContextUtils#getTimeZone
*/
@Nullable
public TimeZone getTimeZone() {
return (this.timeZone != null ? this.timeZone : getFallbackTimeZone());
}
/**
* Determine the fallback locale for this context.
*
The default implementation checks for a JSTL locale attribute in request, session
* or application scope; if not found, returns the {@code HttpServletRequest.getLocale()}.
* @return the fallback locale (never {@code null})
* @see jakarta.servlet.http.HttpServletRequest#getLocale()
*/
protected Locale getFallbackLocale() {
if (jstlPresent) {
Locale locale = JstlLocaleResolver.getJstlLocale(getRequest(), getServletContext());
if (locale != null) {
return locale;
}
}
return getRequest().getLocale();
}
/**
* Determine the fallback time zone for this context.
*
The default implementation checks for a JSTL time zone attribute in request,
* session or application scope; returns {@code null} if not found.
* @return the fallback time zone (or {@code null} if none derivable from the request)
*/
@Nullable
protected TimeZone getFallbackTimeZone() {
if (jstlPresent) {
TimeZone timeZone = JstlLocaleResolver.getJstlTimeZone(getRequest(), getServletContext());
if (timeZone != null) {
return timeZone;
}
}
return null;
}
/**
* Change the current locale to the specified one,
* storing the new locale through the configured {@link LocaleResolver}.
* @param locale the new locale
* @see LocaleResolver#setLocale
* @see #changeLocale(java.util.Locale, java.util.TimeZone)
*/
public void changeLocale(Locale locale) {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(this.request);
if (localeResolver == null) {
throw new IllegalStateException("Cannot change locale if no LocaleResolver configured");
}
localeResolver.setLocale(this.request, this.response, locale);
this.locale = locale;
}
/**
* Change the current locale to the specified locale and time zone context,
* storing the new locale context through the configured {@link LocaleResolver}.
* @param locale the new locale
* @param timeZone the new time zone
* @see LocaleContextResolver#setLocaleContext
* @see org.springframework.context.i18n.SimpleTimeZoneAwareLocaleContext
*/
public void changeLocale(Locale locale, TimeZone timeZone) {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(this.request);
if (!(localeResolver instanceof LocaleContextResolver localeContextResolver)) {
throw new IllegalStateException("Cannot change locale context if no LocaleContextResolver configured");
}
localeContextResolver.setLocaleContext(this.request, this.response,
new SimpleTimeZoneAwareLocaleContext(locale, timeZone));
this.locale = locale;
this.timeZone = timeZone;
}
/**
* Return the current theme (never {@code null}).
*
Resolved lazily for more efficiency when theme support is not being used.
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated(since = "6.0")
public org.springframework.ui.context.Theme getTheme() {
if (this.theme == null) {
// Lazily determine theme to use for this RequestContext.
this.theme = RequestContextUtils.getTheme(this.request);
if (this.theme == null) {
// No ThemeResolver and ThemeSource available -> try fallback.
this.theme = getFallbackTheme();
}
}
return this.theme;
}
/**
* Determine the fallback theme for this context.
*
The default implementation returns the default theme (with name "theme").
* @return the fallback theme (never {@code null})
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated
protected org.springframework.ui.context.Theme getFallbackTheme() {
org.springframework.ui.context.ThemeSource themeSource = RequestContextUtils.getThemeSource(getRequest());
if (themeSource == null) {
themeSource = new org.springframework.ui.context.support.ResourceBundleThemeSource();
}
org.springframework.ui.context.Theme theme = themeSource.getTheme(DEFAULT_THEME_NAME);
if (theme == null) {
throw new IllegalStateException("No theme defined and no fallback theme found");
}
return theme;
}
/**
* Change the current theme to the specified one,
* storing the new theme name through the configured
* {@link org.springframework.web.servlet.ThemeResolver ThemeResolver}.
* @param theme the new theme
* @see org.springframework.web.servlet.ThemeResolver#setThemeName
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated(since = "6.0")
public void changeTheme(@Nullable org.springframework.ui.context.Theme theme) {
org.springframework.web.servlet.ThemeResolver themeResolver = RequestContextUtils.getThemeResolver(this.request);
if (themeResolver == null) {
throw new IllegalStateException("Cannot change theme if no ThemeResolver configured");
}
themeResolver.setThemeName(this.request, this.response, (theme != null ? theme.getName() : null));
this.theme = theme;
}
/**
* Change the current theme to the specified theme by name,
* storing the new theme name through the configured
* {@link org.springframework.web.servlet.ThemeResolver ThemeResolver}.
* @param themeName the name of the new theme
* @see org.springframework.web.servlet.ThemeResolver#setThemeName
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated
public void changeTheme(String themeName) {
org.springframework.web.servlet.ThemeResolver themeResolver = RequestContextUtils.getThemeResolver(this.request);
if (themeResolver == null) {
throw new IllegalStateException("Cannot change theme if no ThemeResolver configured");
}
themeResolver.setThemeName(this.request, this.response, themeName);
// Ask for re-resolution on next getTheme call.
this.theme = null;
}
/**
* (De)activate default HTML escaping for messages and errors, for the scope of this RequestContext.
*
The default is the application-wide setting (the "defaultHtmlEscape" context-param in web.xml).
* @see org.springframework.web.util.WebUtils#getDefaultHtmlEscape
*/
public void setDefaultHtmlEscape(boolean defaultHtmlEscape) {
this.defaultHtmlEscape = defaultHtmlEscape;
}
/**
* Is default HTML escaping active? Falls back to {@code false} in case of no explicit default given.
*/
public boolean isDefaultHtmlEscape() {
return (this.defaultHtmlEscape != null && this.defaultHtmlEscape.booleanValue());
}
/**
* Return the default HTML escape setting, differentiating between no default specified and an explicit value.
* @return whether default HTML escaping is enabled (null = no explicit default)
*/
@Nullable
public Boolean getDefaultHtmlEscape() {
return this.defaultHtmlEscape;
}
/**
* Is HTML escaping using the response encoding by default?
* If enabled, only XML markup significant characters will be escaped with UTF-* encodings.
*
Falls back to {@code true} in case of no explicit default given, as of Spring 4.2.
* @since 4.1.2
*/
public boolean isResponseEncodedHtmlEscape() {
return (this.responseEncodedHtmlEscape == null || this.responseEncodedHtmlEscape.booleanValue());
}
/**
* Return the default setting about use of response encoding for HTML escape setting,
* differentiating between no default specified and an explicit value.
* @return whether default use of response encoding HTML escaping is enabled (null = no explicit default)
* @since 4.1.2
*/
@Nullable
public Boolean getResponseEncodedHtmlEscape() {
return this.responseEncodedHtmlEscape;
}
/**
* Set the UrlPathHelper to use for context path and request URI decoding.
* Can be used to pass a shared UrlPathHelper instance in.
*
A default UrlPathHelper is always available.
*/
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
this.urlPathHelper = urlPathHelper;
}
/**
* Return the UrlPathHelper used for context path and request URI decoding.
* Can be used to configure the current UrlPathHelper.
*
A default UrlPathHelper is always available.
*/
public UrlPathHelper getUrlPathHelper() {
return this.urlPathHelper;
}
/**
* Return the RequestDataValueProcessor instance to use obtained from the
* WebApplicationContext under the name {@code "requestDataValueProcessor"}.
* Or {@code null} if no matching bean was found.
*/
@Nullable
public RequestDataValueProcessor getRequestDataValueProcessor() {
return this.requestDataValueProcessor;
}
/**
* Return the context path of the original request, that is, the path that
* indicates the current web application. This is useful for building links
* to other resources within the application.
*
Delegates to the UrlPathHelper for decoding.
* @see jakarta.servlet.http.HttpServletRequest#getContextPath
* @see #getUrlPathHelper
*/
public String getContextPath() {
return this.urlPathHelper.getOriginatingContextPath(this.request);
}
/**
* Return a context-aware URl for the given relative URL.
* @param relativeUrl the relative URL part
* @return a URL that points back to the server with an absolute path (also URL-encoded accordingly)
*/
public String getContextUrl(String relativeUrl) {
String url = getContextPath() + relativeUrl;
if (this.response != null) {
url = this.response.encodeURL(url);
}
return url;
}
/**
* Return a context-aware URl for the given relative URL with placeholders (named keys with braces {@code {}}).
* For example, send in a relative URL {@code foo/{bar}?spam={spam}} and a parameter map
* {@code {bar=baz,spam=nuts}} and the result will be {@code [contextpath]/foo/baz?spam=nuts}.
* @param relativeUrl the relative URL part
* @param params a map of parameters to insert as placeholders in the url
* @return a URL that points back to the server with an absolute path (also URL-encoded accordingly)
*/
public String getContextUrl(String relativeUrl, Map params) {
String url = getContextPath() + relativeUrl;
url = UriComponentsBuilder.fromUriString(url).buildAndExpand(params).encode().toUri().toASCIIString();
if (this.response != null) {
url = this.response.encodeURL(url);
}
return url;
}
/**
* Return the path to URL mappings within the current servlet including the
* context path and the servlet path of the original request. This is useful
* for building links to other resources within the application where a
* servlet mapping of the style {@code "/main/*"} is used.
* Delegates to the UrlPathHelper to determine the context and servlet path.
*/
public String getPathToServlet() {
String path = this.urlPathHelper.getOriginatingContextPath(this.request);
if (StringUtils.hasText(this.urlPathHelper.getPathWithinServletMapping(this.request))) {
path += this.urlPathHelper.getOriginatingServletPath(this.request);
}
return path;
}
/**
* Return the request URI of the original request, that is, the invoked URL
* without parameters. This is particularly useful as HTML form action target,
* possibly in combination with the original query string.
*
Delegates to the UrlPathHelper for decoding.
* @see #getQueryString
* @see org.springframework.web.util.UrlPathHelper#getOriginatingRequestUri
* @see #getUrlPathHelper
*/
public String getRequestUri() {
return this.urlPathHelper.getOriginatingRequestUri(this.request);
}
/**
* Return the query string of the current request, that is, the part after
* the request path. This is particularly useful for building an HTML form
* action target in combination with the original request URI.
*
Delegates to the UrlPathHelper for decoding.
* @see #getRequestUri
* @see org.springframework.web.util.UrlPathHelper#getOriginatingQueryString
* @see #getUrlPathHelper
*/
public String getQueryString() {
return this.urlPathHelper.getOriginatingQueryString(this.request);
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code the code of the message
* @param defaultMessage the String to return if the lookup fails
* @return the message
*/
public String getMessage(String code, String defaultMessage) {
return getMessage(code, null, defaultMessage, isDefaultHtmlEscape());
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code the code of the message
* @param args arguments for the message, or {@code null} if none
* @param defaultMessage the String to return if the lookup fails
* @return the message
*/
public String getMessage(String code, @Nullable Object[] args, String defaultMessage) {
return getMessage(code, args, defaultMessage, isDefaultHtmlEscape());
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code the code of the message
* @param args arguments for the message as a List, or {@code null} if none
* @param defaultMessage the String to return if the lookup fails
* @return the message
*/
public String getMessage(String code, @Nullable List args, String defaultMessage) {
return getMessage(code, (args != null ? args.toArray() : null), defaultMessage, isDefaultHtmlEscape());
}
/**
* Retrieve the message for the given code.
* @param code the code of the message
* @param args arguments for the message, or {@code null} if none
* @param defaultMessage the String to return if the lookup fails
* @param htmlEscape if the message should be HTML-escaped
* @return the message
*/
public String getMessage(String code, @Nullable Object[] args, String defaultMessage, boolean htmlEscape) {
String msg = this.webApplicationContext.getMessage(code, args, defaultMessage, getLocale());
if (msg == null) {
return "";
}
return (htmlEscape ? HtmlUtils.htmlEscape(msg) : msg);
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code the code of the message
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public String getMessage(String code) throws NoSuchMessageException {
return getMessage(code, null, isDefaultHtmlEscape());
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code the code of the message
* @param args arguments for the message, or {@code null} if none
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public String getMessage(String code, @Nullable Object[] args) throws NoSuchMessageException {
return getMessage(code, args, isDefaultHtmlEscape());
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code the code of the message
* @param args arguments for the message as a List, or {@code null} if none
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public String getMessage(String code, @Nullable List args) throws NoSuchMessageException {
return getMessage(code, (args != null ? args.toArray() : null), isDefaultHtmlEscape());
}
/**
* Retrieve the message for the given code.
* @param code the code of the message
* @param args arguments for the message, or {@code null} if none
* @param htmlEscape if the message should be HTML-escaped
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public String getMessage(String code, @Nullable Object[] args, boolean htmlEscape) throws NoSuchMessageException {
String msg = this.webApplicationContext.getMessage(code, args, getLocale());
return (htmlEscape ? HtmlUtils.htmlEscape(msg) : msg);
}
/**
* Retrieve the given MessageSourceResolvable (e.g. an ObjectError instance), using the "defaultHtmlEscape" setting.
* @param resolvable the MessageSourceResolvable
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public String getMessage(MessageSourceResolvable resolvable) throws NoSuchMessageException {
return getMessage(resolvable, isDefaultHtmlEscape());
}
/**
* Retrieve the given MessageSourceResolvable (e.g. an ObjectError instance).
* @param resolvable the MessageSourceResolvable
* @param htmlEscape if the message should be HTML-escaped
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public String getMessage(MessageSourceResolvable resolvable, boolean htmlEscape) throws NoSuchMessageException {
String msg = this.webApplicationContext.getMessage(resolvable, getLocale());
return (htmlEscape ? HtmlUtils.htmlEscape(msg) : msg);
}
/**
* Retrieve the theme message for the given code.
*
Note that theme messages are never HTML-escaped, as they typically denote
* theme-specific resource paths and not client-visible messages.
* @param code the code of the message
* @param defaultMessage the String to return if the lookup fails
* @return the message
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated
public String getThemeMessage(String code, String defaultMessage) {
String msg = getTheme().getMessageSource().getMessage(code, null, defaultMessage, getLocale());
return (msg != null ? msg : "");
}
/**
* Retrieve the theme message for the given code.
*
Note that theme messages are never HTML-escaped, as they typically denote
* theme-specific resource paths and not client-visible messages.
* @param code the code of the message
* @param args arguments for the message, or {@code null} if none
* @param defaultMessage the String to return if the lookup fails
* @return the message
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated
public String getThemeMessage(String code, @Nullable Object[] args, String defaultMessage) {
String msg = getTheme().getMessageSource().getMessage(code, args, defaultMessage, getLocale());
return (msg != null ? msg : "");
}
/**
* Retrieve the theme message for the given code.
*
Note that theme messages are never HTML-escaped, as they typically denote
* theme-specific resource paths and not client-visible messages.
* @param code the code of the message
* @param args arguments for the message as a List, or {@code null} if none
* @param defaultMessage the String to return if the lookup fails
* @return the message
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated
public String getThemeMessage(String code, @Nullable List args, String defaultMessage) {
String msg = getTheme().getMessageSource().getMessage(code, (args != null ? args.toArray() : null),
defaultMessage, getLocale());
return (msg != null ? msg : "");
}
/**
* Retrieve the theme message for the given code.
*
Note that theme messages are never HTML-escaped, as they typically denote
* theme-specific resource paths and not client-visible messages.
* @param code the code of the message
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated
public String getThemeMessage(String code) throws NoSuchMessageException {
return getTheme().getMessageSource().getMessage(code, null, getLocale());
}
/**
* Retrieve the theme message for the given code.
*
Note that theme messages are never HTML-escaped, as they typically denote
* theme-specific resource paths and not client-visible messages.
* @param code the code of the message
* @param args arguments for the message, or {@code null} if none
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated
public String getThemeMessage(String code, @Nullable Object[] args) throws NoSuchMessageException {
return getTheme().getMessageSource().getMessage(code, args, getLocale());
}
/**
* Retrieve the theme message for the given code.
*
Note that theme messages are never HTML-escaped, as they typically denote
* theme-specific resource paths and not client-visible messages.
* @param code the code of the message
* @param args arguments for the message as a List, or {@code null} if none
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated
public String getThemeMessage(String code, @Nullable List args) throws NoSuchMessageException {
return getTheme().getMessageSource().getMessage(code, (args != null ? args.toArray() : null), getLocale());
}
/**
* Retrieve the given MessageSourceResolvable in the current theme.
*
Note that theme messages are never HTML-escaped, as they typically denote
* theme-specific resource paths and not client-visible messages.
* @param resolvable the MessageSourceResolvable
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
* @deprecated as of 6.0, with no direct replacement
*/
@Deprecated
public String getThemeMessage(MessageSourceResolvable resolvable) throws NoSuchMessageException {
return getTheme().getMessageSource().getMessage(resolvable, getLocale());
}
/**
* Retrieve the Errors instance for the given bind object, using the "defaultHtmlEscape" setting.
* @param name the name of the bind object
* @return the Errors instance, or {@code null} if not found
*/
@Nullable
public Errors getErrors(String name) {
return getErrors(name, isDefaultHtmlEscape());
}
/**
* Retrieve the Errors instance for the given bind object.
* @param name the name of the bind object
* @param htmlEscape create an Errors instance with automatic HTML escaping?
* @return the Errors instance, or {@code null} if not found
*/
@Nullable
public Errors getErrors(String name, boolean htmlEscape) {
if (this.errorsMap == null) {
this.errorsMap = new HashMap<>();
}
Errors errors = this.errorsMap.get(name);
boolean put = false;
if (errors == null) {
errors = (Errors) getModelObject(BindingResult.MODEL_KEY_PREFIX + name);
// Check old BindException prefix for backwards compatibility.
if (errors instanceof BindException bindException) {
errors = bindException.getBindingResult();
}
if (errors == null) {
return null;
}
put = true;
}
if (htmlEscape && !(errors instanceof EscapedErrors)) {
errors = new EscapedErrors(errors);
put = true;
}
else if (!htmlEscape && errors instanceof EscapedErrors escapedErrors) {
errors = escapedErrors.getSource();
put = true;
}
if (put) {
this.errorsMap.put(name, errors);
}
return errors;
}
/**
* Retrieve the model object for the given model name, either from the model
* or from the request attributes.
* @param modelName the name of the model object
* @return the model object
*/
@Nullable
protected Object getModelObject(String modelName) {
if (this.model != null) {
return this.model.get(modelName);
}
else {
return this.request.getAttribute(modelName);
}
}
/**
* Create a BindStatus for the given bind object, using the "defaultHtmlEscape" setting.
* @param path the bean and property path for which values and errors will be resolved (e.g. "person.age")
* @return the new BindStatus instance
* @throws IllegalStateException if no corresponding Errors object found
*/
public BindStatus getBindStatus(String path) throws IllegalStateException {
return new BindStatus(this, path, isDefaultHtmlEscape());
}
/**
* Create a BindStatus for the given bind object, using the "defaultHtmlEscape" setting.
* @param path the bean and property path for which values and errors will be resolved (e.g. "person.age")
* @param htmlEscape create a BindStatus with automatic HTML escaping?
* @return the new BindStatus instance
* @throws IllegalStateException if no corresponding Errors object found
*/
public BindStatus getBindStatus(String path, boolean htmlEscape) throws IllegalStateException {
return new BindStatus(this, path, htmlEscape);
}
/**
* Inner class that isolates the JSTL dependency.
* Just called to resolve the fallback locale if the JSTL API is present.
*/
private static class JstlLocaleResolver {
@Nullable
public static Locale getJstlLocale(HttpServletRequest request, @Nullable ServletContext servletContext) {
Object localeObject = Config.get(request, Config.FMT_LOCALE);
if (localeObject == null) {
HttpSession session = request.getSession(false);
if (session != null) {
localeObject = Config.get(session, Config.FMT_LOCALE);
}
if (localeObject == null && servletContext != null) {
localeObject = Config.get(servletContext, Config.FMT_LOCALE);
}
}
return (localeObject instanceof Locale locale ? locale : null);
}
@Nullable
public static TimeZone getJstlTimeZone(HttpServletRequest request, @Nullable ServletContext servletContext) {
Object timeZoneObject = Config.get(request, Config.FMT_TIME_ZONE);
if (timeZoneObject == null) {
HttpSession session = request.getSession(false);
if (session != null) {
timeZoneObject = Config.get(session, Config.FMT_TIME_ZONE);
}
if (timeZoneObject == null && servletContext != null) {
timeZoneObject = Config.get(servletContext, Config.FMT_TIME_ZONE);
}
}
return (timeZoneObject instanceof TimeZone timeZone ? timeZone : null);
}
}
}