All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.web.reactive.result.view.UrlBasedViewResolver Maven / Gradle / Ivy

/*
 * Copyright 2002-2017 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
 *
 *      http://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.reactive.result.view;

import java.util.Locale;
import java.util.function.Function;

import reactor.core.publisher.Mono;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;

/**
 * A {@link ViewResolver} that allow direct resolution of symbolic view names
 * to URLs without explicit mapping definition. This is useful if symbolic names
 * match the names of view resources in a straightforward manner (i.e. the
 * symbolic name is the unique part of the resource's filename), without the need
 * for a dedicated mapping to be defined for each view.
 *
 * 

Supports {@link AbstractUrlBasedView} subclasses like * {@link org.springframework.web.reactive.result.view.freemarker.FreeMarkerView}. * The view class for all views generated by this resolver can be specified * via the "viewClass" property. * *

View names can either be resource URLs themselves, or get augmented by a * specified prefix and/or suffix. Exporting an attribute that holds the * RequestContext to all views is explicitly supported. * *

Example: prefix="templates/", suffix=".ftl", viewname="test" -> * "templates/test.ftl" * *

As a special feature, redirect URLs can be specified via the "redirect:" * prefix. E.g.: "redirect:myAction" will trigger a redirect to the given * URL, rather than resolution as standard view name. This is typically used * for redirecting to a controller URL after finishing a form workflow. * *

Note: This class does not support localized resolution, i.e. resolving * a symbolic view name to different resources depending on the current locale. * * @author Rossen Stoyanchev * @author Sebastien Deleuze * @author Juergen Hoeller * @since 5.0 */ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewResolver, ApplicationContextAware, InitializingBean { /** * Prefix for special view names that specify a redirect URL (usually * to a controller after a form has been submitted and processed). * Such view names will not be resolved in the configured default * way but rather be treated as special shortcut. */ public static final String REDIRECT_URL_PREFIX = "redirect:"; @Nullable private Class viewClass; private String prefix = ""; private String suffix = ""; @Nullable private String[] viewNames; private Function redirectViewProvider = RedirectView::new; @Nullable private String requestContextAttribute; @Nullable private ApplicationContext applicationContext; /** * Set the view class to instantiate through {@link #createView(String)}. * @param viewClass a class that is assignable to the required view class * which by default is AbstractUrlBasedView */ public void setViewClass(@Nullable Class viewClass) { if (viewClass != null && !requiredViewClass().isAssignableFrom(viewClass)) { String name = viewClass.getName(); throw new IllegalArgumentException("Given view class [" + name + "] " + "is not of type [" + requiredViewClass().getName() + "]"); } this.viewClass = viewClass; } /** * Return the view class to be used to create views. */ @Nullable protected Class getViewClass() { return this.viewClass; } /** * Return the required type of view for this resolver. * This implementation returns {@link AbstractUrlBasedView}. * @see AbstractUrlBasedView */ protected Class requiredViewClass() { return AbstractUrlBasedView.class; } /** * Set the prefix that gets prepended to view names when building a URL. */ public void setPrefix(@Nullable String prefix) { this.prefix = (prefix != null ? prefix : ""); } /** * Return the prefix that gets prepended to view names when building a URL. */ protected String getPrefix() { return this.prefix; } /** * Set the suffix that gets appended to view names when building a URL. */ public void setSuffix(@Nullable String suffix) { this.suffix = (suffix != null ? suffix : ""); } /** * Return the suffix that gets appended to view names when building a URL. */ protected String getSuffix() { return this.suffix; } /** * Set the view names (or name patterns) that can be handled by this * {@link ViewResolver}. View names can contain simple wildcards such that * 'my*', '*Report' and '*Repo*' will all match the view name 'myReport'. * @see #canHandle */ public void setViewNames(@Nullable String... viewNames) { this.viewNames = viewNames; } /** * Return the view names (or name patterns) that can be handled by this * {@link ViewResolver}. */ @Nullable protected String[] getViewNames() { return this.viewNames; } /** * URL based {@link RedirectView} provider which can be used to provide, for example, * redirect views with a custom default status code. */ public void setRedirectViewProvider(Function redirectViewProvider) { this.redirectViewProvider = redirectViewProvider; } /** * Set the name of the {@link RequestContext} attribute for all views. * @param requestContextAttribute name of the RequestContext attribute * @see AbstractView#setRequestContextAttribute */ public void setRequestContextAttribute(@Nullable String requestContextAttribute) { this.requestContextAttribute = requestContextAttribute; } /** * Return the name of the @link RequestContext} attribute for all views, if any. */ @Nullable protected String getRequestContextAttribute() { return this.requestContextAttribute; } /** * Accept the containing {@code ApplicationContext}, if any. *

To be used for the initialization of newly created {@link View} instances, * applying lifecycle callbacks and providing access to the containing environment. * @see #setViewClass * @see #createView * @see #applyLifecycleMethods */ @Override public void setApplicationContext(@Nullable ApplicationContext applicationContext) { this.applicationContext = applicationContext; } /** * Return the containing {@code ApplicationContext}, if any. * @see #setApplicationContext */ @Nullable public ApplicationContext getApplicationContext() { return this.applicationContext; } @Override public void afterPropertiesSet() throws Exception { if (getViewClass() == null) { throw new IllegalArgumentException("Property 'viewClass' is required"); } } @Override public Mono resolveViewName(String viewName, Locale locale) { if (!canHandle(viewName, locale)) { return Mono.empty(); } AbstractUrlBasedView urlBasedView; if (viewName.startsWith(REDIRECT_URL_PREFIX)) { String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length()); urlBasedView = this.redirectViewProvider.apply(redirectUrl); } else { urlBasedView = createView(viewName); } View view = applyLifecycleMethods(viewName, urlBasedView); try { return (urlBasedView.checkResourceExists(locale) ? Mono.just(view) : Mono.empty()); } catch (Exception ex) { return Mono.error(ex); } } /** * Indicates whether or not this {@link ViewResolver} can handle the supplied * view name. If not, an empty result is returned. The default implementation * checks against the configured {@link #setViewNames view names}. * @param viewName the name of the view to retrieve * @param locale the Locale to retrieve the view for * @return whether this resolver applies to the specified view * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String) */ protected boolean canHandle(String viewName, Locale locale) { String[] viewNames = getViewNames(); return (viewNames == null || PatternMatchUtils.simpleMatch(viewNames, viewName)); } /** * Creates a new View instance of the specified view class and configures it. * Does not perform any lookup for pre-defined View instances. *

Spring lifecycle methods as defined by the bean container do not have to * be called here: They will be automatically applied afterwards, provided * that an {@link #setApplicationContext ApplicationContext} is available. * @param viewName the name of the view to build * @return the View instance * @see #getViewClass() * @see #applyLifecycleMethods */ protected AbstractUrlBasedView createView(String viewName) { Class viewClass = getViewClass(); Assert.state(viewClass != null, "No view class"); AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass); view.setSupportedMediaTypes(getSupportedMediaTypes()); view.setRequestContextAttribute(getRequestContextAttribute()); view.setDefaultCharset(getDefaultCharset()); view.setUrl(getPrefix() + viewName + getSuffix()); return view; } /** * Apply the containing {@link ApplicationContext}'s lifecycle methods * to the given {@link View} instance, if such a context is available. * @param viewName the name of the view * @param view the freshly created View instance, pre-configured with * {@link AbstractUrlBasedView}'s properties * @return the {@link View} instance to use (either the original one * or a decorated variant) * @see #getApplicationContext() * @see ApplicationContext#getAutowireCapableBeanFactory() * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#initializeBean */ protected View applyLifecycleMethods(String viewName, AbstractUrlBasedView view) { ApplicationContext context = getApplicationContext(); if (context != null) { Object initialized = context.getAutowireCapableBeanFactory().initializeBean(view, viewName); if (initialized instanceof View) { return (View) initialized; } } return view; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy