18.4处理程序映射
在以前的Spring版本中,用户需要HandlerMapping
在Web应用程序上下文中定义一个或多个 bean,以将传入的Web请求映射到适当的处理程序。通过引入注释控制器,您通常不需要这样做,因为它RequestMappingHandlerMapping
会@RequestMapping
自动在所有@Controller
bean 上查找 注释。但是,请记住,所有HandlerMapping
扩展的类AbstractHandlerMapping
都具有以下可用于自定义行为的属性:
-
interceptors
要使用的拦截器列表。HandlerInterceptor
在 第18.4.1节“使用HandlerInterceptor拦截请求”中讨论。 -
defaultHandler
当这个处理程序映射不会导致一个匹配的处理程序时,使用默认处理程序。 -
order
基于order属性的值(参见org.springframework.core.Ordered
接口),Spring会排序上下文中可用的所有处理程序映射,并应用第一个匹配处理程序。 -
alwaysUseFullPath
如果true
Spring使用当前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>
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文件,例如,您只能在办公时间内访问该网站。
当使用RequestMappingHandlerMapping 实际处理程序时,HandlerMethod 它的一个实例 标识将被调用的特定控制器方法。 |
您可以看到,Spring适配器类HandlerInterceptorAdapter
可以更容易地扩展HandlerInterceptor
接口。
在上面的示例中,配置的拦截器将应用于使用注释控制器方法处理的所有请求。如果要缩小拦截器应用的URL路径,可以使用MVC命名空间或MVC Java配置,或声明类型的bean实例MappedInterceptor 。请参见第18.16.1节“启用MVC Java配置或MVC XML命名空间”。 |
请注意,该postHandle
方法HandlerInterceptor
并不总是非常适用于@ResponseBody
和ResponseEntity
方法。在这种情况下HttpMessageConverter
,在postHandle
调用之前写入并提交响应,这使得不可能更改响应,例如添加标题。相反,应用程序可以实现 ResponseBodyAdvice
并将其声明为@ControllerAdvice
bean或直接配置它RequestMappingHandlerAdapter
。
用于Web应用程序的所有MVC框架提供了一种解决视图的方法。Spring提供视图解析器,使您可以在浏览器中渲染模型,而不需要将您视为特定的视图技术。开箱即用,例如,Spring允许您使用JSP,FreeMarker模板和XSLT视图。请参见第19章,查看技术对于如何整合并使用不同的视图技术的讨论。
对于Spring处理视图的方式来说重要的两个接口是ViewResolver
和View
。所述ViewResolver
提供视图名称和实际视图之间的映射。该View
接口解决了请求的准备,并将请求交给一种视图技术。
正如在讨论第18.3节,“实施控制器”,在Spring Web MVC框架控制器的所有处理方法必须解析为一个逻辑视图名称,明确地(例如,通过返回 String
,View
或ModelAndView
)或隐式(即基于惯例)。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)和子类,如JstlView 和TilesView 。您可以通过使用为此解析器生成的所有视图指定视图类 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。示例可以在下一章中找到,涵盖视图技术。您可以看到,您可以识别父视图,从属性文件中的所有视图“扩展”。这样,您可以指定默认视图类。
AbstractCachingViewResolver 他们解析的缓存视图实例的子类。缓存提高了某些视图技术的性能。可以通过将cache 属性设置为关闭缓存false 。此外,如果您必须在运行时刷新某个视图(例如,当FreeMarker模板被修改时),则可以使用该removeFromCache(String viewName, Locale loc) 方法。 |
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
意志总是返回一个视图!
如前所述,控制器通常返回逻辑视图名称,视图解析器解析为特定视图技术。对于视图技术如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
数据。
作为控制器响应的结果强制重定向的一种方法是控制器创建并返回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)的视图名称。因此,这个前缀对于InternalResourceViewResolver
和InternalResourceView
(对于JSP而言)不是有用的。但是,当您主要使用另一种视图技术时,前缀可能会有所帮助,但是仍然希望强制转发由Servlet / JSP引擎处理的资源。(请注意,您也可以链接多个视图解析器。)
与redirect:
前缀一样,如果具有前缀的视图名称forward:
注入到控制器中,则控制器在处理响应方面没有检测到发生任何特殊事件。