18.5解决观点

用于Web应用程序的所有MVC框架提供了一种解决视图的方法。Spring提供视图解析器,使您可以在浏览器中渲染模型,而不需要将您视为特定的视图技术。开箱即用,例如,Spring允许您使用JSP,FreeMarker模板和XSLT视图。请参见第19章,查看技术对于如何整合并使用不同的视图技术的讨论。

对于Spring处理视图的方式来说重要的两个接口是ViewResolverView。所述ViewResolver提供视图名称和实际视图之间的映射。该View接口解决了请求的准备,并将请求交给一种视图技术。

18.5.1使用ViewResolver界面解析视图

正如在讨论第18.3节,“实施控制器”,在Spring Web MVC框架控制器的所有处理方法必须解析为一个逻辑视图名称,明确地(例如,通过返回StringViewModelAndView)或隐式(即基于惯例)。Spring中的视图由逻辑视图名称解析,并由视图解析器解析。春天有不少视角解析器。这张表列出了大部分; 以下几个例子。

表18.3. 查看解析器

视图解析器 描述
AbstractCachingViewResolver 抽象视图解析器缓存视图。通常情况下,需要准备才能使用; 扩展此视图解析器提供缓存。
XmlViewResolver 实现ViewResolver它接受使用与Spring的XML bean工厂相同的DTD使用XML编写的配置文件。默认配置文件是/WEB-INF/views.xml
ResourceBundleViewResolver 实现ViewResolver它使用bean定义ResourceBundle,由bundle基本名称指定。通常,您可以在属性文件中定义bundle,该属性文件位于类路径中。默认文件名是views.properties
UrlBasedViewResolver 简单地实现ViewResolver了直接解析逻辑视图名称到URL的接口,而没有明确的映射定义。如果您的逻辑名称以直观的方式与视图资源的名称匹配,则这是适当的,而不需要任意映射。
InternalResourceViewResolver 方便的子类UrlBasedViewResolver支持InternalResourceView(实际上是Servlet和JSP)和子类,如JstlViewTilesView。您可以通过使用为此解析器生成的所有视图指定视图类setViewClass(..)。有关UrlBasedViewResolver详细信息,请参阅javadoc。
FreeMarkerViewResolver 它的便利子类UrlBasedViewResolver支持FreeMarkerView和自定义子类。
ContentNegotiatingViewResolver 实现ViewResolver根据请求文件名或Accept头解析视图的界面。请参见第18.5.4节“ContentNegotiatingViewResolver”

例如,使用JSP作为视图技术,可以使用UrlBasedViewResolver。此视图解析器将视图名称转换为URL,并将请求转交给RequestDispatcher以呈现视图。

  1. <bean id="viewResolver"
  2. class="org.springframework.web.servlet.view.UrlBasedViewResolver">
  3. <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
  4. <property name="prefix" value="/WEB-INF/jsp/"/>
  5. <property name="suffix" value=".jsp"/>
  6. </bean>

test以逻辑视图名称返回时,此视图解析器将请求转发到RequestDispatcher将要发送的请求/WEB-INF/jsp/test.jsp

当您在Web应用程序中组合不同的视图技术时,可以使用ResourceBundleViewResolver

  1. <bean id="viewResolver"
  2. class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
  3. <property name="basename" value="views"/>
  4. <property name="defaultParentView" value="parentView"/>
  5. </bean>

ResourceBundleViewResolver考察ResourceBundle确定了基本名字和它应该解决每个视图,它使用属性的值[viewname].(class)作为视图类和属性的值[viewname].url作为视图的URL。示例可以在下一章中找到,涵盖视图技术。您可以看到,您可以识别父视图,从属性文件中的所有视图“扩展”。这样,您可以指定默认视图类。

AbstractCachingViewResolver他们解析的缓存视图实例的子类。缓存提高了某些视图技术的性能。可以通过将cache属性设置为关闭缓存false。此外,如果您必须在运行时刷新某个视图(例如,当FreeMarker模板被修改时),则可以使用该removeFromCache(String viewName, Locale loc)方法。

18.5.2链接视图解析器

Spring支持多个视图解析器。因此,您可以链接解析器,并且在某些情况下例如覆盖特定视图。您可以通过在应用程序上下文中添加多个解析器来链接视图解析器,如有必要,可以通过设置order属性来指定排序。记住,order属性越高,视图解析器在链中的位置越晚。

在以下示例中,视图解析器由两个解析器组成,一个InternalResourceViewResolver始终自动定位为链中的最后一个解析器,另一个XmlViewResolver用于指定Excel视图。Excel不支持Excel视图InternalResourceViewResolver

  1. <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  2. <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
  3. <property name="prefix" value="/WEB-INF/jsp/"/>
  4. <property name="suffix" value=".jsp"/>
  5. </bean>
  6. <bean id="excelViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
  7. <property name="order" value="1"/>
  8. <property name="location" value="/WEB-INF/views.xml"/>
  9. </bean>
  10. <!-- in views.xml -->
  11. <beans>
  12. <bean name="report" class="org.springframework.example.ReportExcelView"/>
  13. </beans>

如果一个特定的视图解析器不会产生视图,Spring会检查其他视图解析器的上下文。如果存在另外的视图解析器,Spring会继续检查它们,直到视图解决。如果没有视图解析器返回一个视图,Spring会抛出一个ServletException

视图解析器的合同指定视图解析器可以返回null以指示无法找到视图。然而,并不是所有的视图解析器都这样做,因为在某些情况下,解析器根本无法检测视图是否存在。例如,内部InternalResourceViewResolver使用RequestDispatcher,分派是确定JSP是否存在的唯一方法,但此操作只能执行一次。对于FreeMarkerViewResolver其他一些人也是如此。检查特定视图解析器的javadoc以查看是否报告不存在的视图。因此,把一个InternalResourceViewResolver在链中比在链中的最后结果的其他地方没有得到充分检验,因为InternalResourceViewResolver意志总是返回一个视图!

18.5.3重定向到视图

如前所述,控制器通常返回逻辑视图名称,视图解析器解析为特定视图技术。对于视图技术如JSP,其通过servlet或JSP引擎处理,此分辨率通常是通过组合处理InternalResourceViewResolverInternalResourceView,它发出一个内部正向或包括经由在Servlet API的RequestDispatcher.forward(..)方法或RequestDispatcher.include()方法。对于其他视图技术,如FreeMarker,XSLT等,视图本身将内容直接写入响应流。

在呈现视图之前,有时需要将HTTP重定向发回客户端。这是可取的,例如,当一个控制器已经被调用了POST数据时,并且响应实际上是对另一个控制器的委派(例如,成功的表单提交)。在这种情况下,正常的内部向前将意味着另一个控制器也将看到相同的POST数据,如果它可能与其他预期数据混淆,这是潜在的问题。在显示结果之前执行重定向的另一个原因是消除用户多次提交表单数据的可能性。在这种情况下,浏览器将首先发送一个初始的POST; 然后会收到重定向到其他URL的响应;GET最后浏览器将为重定向响应中指定的URL执行后续操作。因此,从浏览器的角度来看,当前页面并不反映的结果POST,而是一个GET。最终的效果是用户无法POST通过执行刷新来意外重新获得相同的数据。刷新强制GET结果页面a,而不是重新发送初始POST数据。

RedirectView的

作为控制器响应的结果强制重定向的一种方法是控制器创建并返回Spring的实例RedirectView。在这种情况下,DispatcherServlet不使用普通视图分辨机制。而是因为已经给了(重定向)视图,DispatcherServlet简单地指示视图来完成它的工作。将RedirectView依次调用HttpServletResponse.sendRedirect()发送一个HTTP重定向到客户端浏览器。

如果使用RedirectView并且视图由控制器本身创建,则建议您将重定向URL配置为注入到控制器中,以使其不会被烘烤到控制器中,而是在上下文中配置视图名称。在一节“重定向:前缀”有利于这种脱钩。

将数据传递到重定向目标

默认情况下,所有模型属性都被认为是重定向URL中的URI模板变量。在剩余的属性中,原始类型或原始类型的集合/数组的那些属性将自动附加为查询参数。

如果为重定向准备了模型实例,则将原始类型属性作为查询参数附加可能是所需的结果。然而,在注释控制器中,模型可能包含为渲染目的添加的附加属性(例如下拉字段值)。为了避免这种属性出现在URL中的可能性,一种@RequestMapping方法可以声明一个类型的参数,RedirectAttributes并使用它来指定可供使用的确切属性RedirectView。如果方法重定向,则使用内容RedirectAttributes。否则使用模型的内容。

RequestMappingHandlerAdapter提供了一个名为标志"ignoreDefaultModelOnRedirect",可以用来表示默认的内容Model,如果一个控制器方法重定向不应该被使用。相反,控制器方法应该声明一个类型的属性,RedirectAttributes或者如果它不这样做,则不应该传递任何属性RedirectView。MVC命名空间和MVC Java配置都将此标志设置false为保持向后兼容性。但是,对于新的应用程序,我们建议将其设置为true

请注意,当扩展重定向网址时,来自本请求的URI模板变量将自动提供,并且不需要通过Model或不显式添加RedirectAttributes。例如:

  1. @PostMapping("/files/{path}")
  2. public String upload(...) {
  3. // ...
  4. return "redirect:files/{path}";
  5. }

将数据传递到重定向目标的另一种方法是通过Flash属性。与其他重定向属性不同,Flash属性保存在HTTP会话中(因此不会出现在URL中)。有关详细信息,请参见第18.6节“使用Flash属性”

重定向:前缀

虽然使用RedirectView工程正常,如果控制器本身创建RedirectView,则没有避免控制器知道重定向发生的事实。这是非常不合时宜的事情,太紧密地结合在一起。控制器不应该真正关心响应如何处理。一般来说,它应该仅在注入到其中的视图名称的方式操作。

特殊的redirect:前缀允许你完成这个。如果返回具有前缀的视图名称redirect:,则UrlBasedViewResolver(和所有子类)将会将其识别为需要重定向的特殊指示。视图名称的其余部分将被视为重定向网址。

净效果与控制器返回一样RedirectView,但现在控制器本身可以简单地按逻辑视图名称进行操作。一个逻辑视图名称,例如redirect:/myapp/some/resource将重定向到当前的Servlet上下文,而一个名称redirect:http://myhost.com/some/arbitrary/path将重定向到绝对URL。

请注意,控制器处理程序使用注释@ResponseStatus,注释值优先于设置的响应状态RedirectView

转发:前缀

也可以使用forward:最终由子类决定的视图名称的特殊前缀UrlBasedViewResolver。这将创建一个 视图名称InternalResourceView(其最终将RequestDispatcher.forward()围绕其被视为URL)的视图名称。因此,这个前缀对于InternalResourceViewResolverInternalResourceView(对于JSP而言)不是有用的。但是,当您主要使用另一种视图技术时,前缀可能会有所帮助,但是仍然希望强制转发由Servlet / JSP引擎处理的资源。(请注意,您也可以链接多个视图解析器。)

redirect:前缀一样,如果具有前缀的视图名称forward:注入到控制器中,则控制器在处理响应方面没有检测到发生任何特殊事件。

18.5.4 ContentNegotiatingViewResolver

ContentNegotiatingViewResolver不会解析视图本身,而是委托给其他视图解析器,选择类似于客户端请求的表示的视图。客户端可以从服务器请求表示方式存在两种策略:

Accept标题的一个问题是,不可能在HTML中的Web浏览器中设置它。例如,在Firefox中,它被修改为: