《Spring 5 官方文档》18. Web MVC 框架(六)

18.4处理程序映射

在以前的Spring版本中,用户需要HandlerMapping在Web应用程序上下文中定义一个或多个 bean,以将传入的Web请求映射到适当的处理程序。通过引入注释控制器,您通常不需要这样做,因为它RequestMappingHandlerMapping@RequestMapping自动在所有@Controllerbean 上查找 注释。但是,请记住,所有HandlerMapping扩展的类AbstractHandlerMapping都具有以下可用于自定义行为的属性:

  • interceptors要使用的拦截器列表。HandlerInterceptor在 第18.4.1节“使用HandlerInterceptor拦截请求”中讨论
  • defaultHandler 当这个处理程序映射不会导致一个匹配的处理程序时,使用默认处理程序。
  • order基于order属性的值(参见 org.springframework.core.Ordered接口),Spring会排序上下文中可用的所有处理程序映射,并应用第一个匹配处理程序。
  • alwaysUseFullPath如果trueSpring使用当前Servlet上下文中的完整路径来找到一个适当的处理程序。如果false(默认值),则使用当前Servlet映射中的路径。例如,如果使用Servlet /testing/*并将alwaysUseFullPath属性设置为true, /testing/viewPage.html则使用该属性,而如果该属性设置为false/viewPage.html
  • urlDecode默认为true,从Spring 2.5开始。如果您喜欢比较编码路径,请将此标志设置为false。但是,HttpServletRequest始终以解码形式公开Servlet路径。请注意,与编码路径相比,Servlet路径将不匹配。

以下示例显示如何配置拦截器:

<bean> 
	<bean  class = “org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping” > 
		<property  name = “interceptors” > 
			<bean  class = “example.MyInterceptor” /> 
		</ property> 
	</ bean> 
</ beans>

18.4.1用HandlerInterceptor拦截请求

Spring的处理程序映射机制包括处理程序拦截器,当您希望将特定功能应用于某些请求时,例如,检查主体,这是有用的。

位于处理程序映射中的拦截器必须HandlerInterceptor从 org.springframework.web.servlet包中实现。这个接口定义了三个方法: preHandle(..)被称为被执行的实际处理程序; postHandle(..)被称为执行的处理程序; 并在完成请求完成后afterCompletion(..)调用。这三种方法应提供足够的灵活性进行各种预处理和后处理。

preHandle(..)方法返回一个布尔值。您可以使用此方法来中断或继续处理执行链。当此方法返回true时,处理程序执行链将继续; 当它返回false时,DispatcherServlet 假定拦截器本身已经处理了请求(并且例如呈现适当的视图),并且不会继续执行其他拦截器和执行链中的实际处理程序。

拦截器可以使用interceptors属性进行配置,该属性存在于所有HandlerMapping类中AbstractHandlerMapping。这在下面的示例中显示:

<bean> 
	<bean  id = “handlerMapping” 
			class = “org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping” > 
		<property  name = “interceptors” > 
			<list> 
				<ref  bean = “officeHoursInterceptor” /> 
			</ list> 
		</ property> 
	</ bean>

	<bean  id = “officeHoursInterceptor” 
			class = “samples.TimeBasedAccessInterceptor” > 
		<property  name = “openingTime”  value = “9” /> 
		<property  name = “closingTime”  value = “18” /> 
	</ bean> 
</ beans >
包装样品;

public  class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {

	private  int openingTime;
	private  int closingTime;

	public  void setOpeningTime(int openingTime){
		 this .openingTime = openingTime;
	}

	public  void setClosingTime(int closingTime){
		 this .closingTime = closingTime;
	}

	public  boolean preHandle(HttpServletRequest request,HttpServletResponse response,
			对象处理程序)throws Exception {
		日历cal = Calendar.getInstance();
		int hour = cal.get(HOUR_OF_DAY);
		if(openingTime <= hour && hour <closingTime){
			 return true;
		}
		response.sendRedirect(“http://host.com/outsideOfficeHours.html”);
		返回假
	}
}

该映射处理的任何请求都被截取TimeBasedAccessInterceptor。如果当前时间在办公时间之外,用户将被重定向到静态HTML文件,例如,您只能在办公时间内访问该网站。

《Spring 5 官方文档》18. Web MVC 框架(六)
当使用RequestMappingHandlerMapping实际处理程序时,HandlerMethod它的一个实例 标识将被调用的特定控制器方法。

您可以看到,Spring适配器类HandlerInterceptorAdapter可以更容易地扩展HandlerInterceptor接口。

《Spring 5 官方文档》18. Web MVC 框架(六)
在上面的示例中,配置的拦截器将应用于使用注释控制器方法处理的所有请求。如果要缩小拦截器应用的URL路径,可以使用MVC命名空间或MVC Java配置,或声明类型的bean实例MappedInterceptor。请参见第18.16.1节“启用MVC Java配置或MVC XML命名空间”

请注意,该postHandle方法HandlerInterceptor并不总是非常适用于@ResponseBodyResponseEntity方法。在这种情况下HttpMessageConverter ,在postHandle调用之前写入并提交响应,这使得不可能更改响应,例如添加标题。相反,应用程序可以实现 ResponseBodyAdvice并将其声明为@ControllerAdvicebean或直接配置它RequestMappingHandlerAdapter

18.5解决观点

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

对于Spring处理视图的方式来说重要的两个接口是ViewResolver 和View。所述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以呈现视图。

<bean  id = “viewResolver” 
		class = “org.springframework.web.servlet.view.UrlBasedViewResolver” > 
	<property  name = “viewClass”  value = “org.springframework.web.servlet.view.JstlView” /> 
	<property  name = “prefix”  value = “/ WEB-INF / jsp /” /> 
	<property  name = “suffix”  value = “.jsp” /> 
</ bean>

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

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

<bean  id = “viewResolver” 
		class = “org.springframework.web.servlet.view.ResourceBundleViewResolver” > 
	<property  name = “basename”  value = “views” /> 
	<property  name = “defaultParentView”  value = “parentView” / > 
</ bean>

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

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

18.5.2链接视图解析器

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

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

<bean  id = “jspViewResolver”  class = “org.springframework.web.servlet.view.InternalResourceViewResolver” > 
	<property  name = “viewClass”  value = “org.springframework.web.servlet.view.JstlView” /> 
	<property  name = “prefix”  value = “/ WEB-INF / jsp /” /> 
	<property  name = “suffix”  value = “.jsp” /> 
</ bean>

<bean  id = “excelViewResolver”  class = “org.springframework.web.servlet.view.XmlViewResolver” > 
	<property  name = “order”  value = “1” /> 
	<property  name = “location”  value = “/ WEB- INF / views.xml“ /> 
</ bean>

<! -  in views.xml  - >

<bean> 
	<bean  name = “report”  class = “org.springframework.example.ReportExcelView” /> 
</ beans>

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

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

18.5.3重定向到视图

如前所述,控制器通常返回逻辑视图名称,视图解析器解析为特定视图技术。对于视图技术如JSP,其通过servlet或JSP引擎处理,此分辨率通常是通过组合处理InternalResourceViewResolver和 InternalResourceView,它发出一个内部正向或包括经由在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。例如:

@PostMapping(“/ files / {path}”)
 public String upload(...){
	 // 
	返回 “redirect:files / {path}” ;
}

将数据传递到重定向目标的另一种方法是通过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:注入到控制器中,则控制器在处理响应方面没有检测到发生任何特殊事件。

上一篇:实用代码-C#获取本机网络适配器信息及MAC地址


下一篇:《GDAL源码剖析与开发指南》一一1.9 简单的调用