这ContentNegotiatingViewResolver不会解析视图本身,而是委托给其他视图解析器,选择类似于客户端请求的表示的视图。客户端可以从服务器请求表示方式存在两种策略:
通常通过在URI中使用不同的文件扩展名为每个资源使用不同的URI。例如,URI http://www.example.com/users/fred.pdf请求用户fred的PDF表示,并http://www.example.com/users/fred.xml请求XML表示。使用相同的URI来为客户端定位资源,但设置AcceptHTTP请求标头以列出它理解的媒体类型。例如,一个HTTP请求, http://www.example.com/users/fred其中一个Accept头设置为application/pdf 请求用户fred的PDF表示,同时 http://www.example.com/users/fred使用Accept头设置来text/xml请求XML表示。这个策略被称为 内容谈判。 Accept标题的一个问题是,不可能在HTML中的Web浏览器中设置它。例如,在Firefox中,它被修改为: 接受:text / html,application / xhtml + xml,application / xml; q = 0.9,* / *; q = 0.8因此,在开发基于浏览器的Web应用程序时,通常会看到每个表示使用不同的URI。
为了支持资源的多个表示,Spring提供了 ContentNegotiatingViewResolver根据AcceptHTTP请求的文件扩展名或头部来解析视图。ContentNegotiatingViewResolver不执行视图分辨率本身,而是委托给您通过bean属性指定的视图解析器的列表ViewResolvers。
在ContentNegotiatingViewResolver选择一个合适的View通过比较与所述媒体类型(也被称为媒体请求类型(一个或多个),以处理该请求 Content-Type由支持)的View与每个其相关联ViewResolvers。View具有兼容性的列表中的第一个将表示Content-Type返回给客户端。如果链条不能提供兼容的视图,则会查看ViewResolver通过DefaultViews属性指定的视图列表。后一个选项适用于Views可以呈现当前资源的适当表示的单例,而不管逻辑视图名称如何。的Accept 报头可以包括通配符,例如text/*,在这种情况下View,其内容类型是text/xml为相容的匹配。
要支持基于文件扩展名的视图的自定义解析,请使用 ContentNegotiationManager:请参见第18.16.6节“内容协商”。
以下是一个示例配置ContentNegotiatingViewResolver:
<bean class = “org.springframework.web.servlet.view.ContentNegotiatingViewResolver” > <property name = “viewResolvers” > <list> <bean class = “org.springframework.web.servlet.view.BeanNameViewResolver” /> <bean class = “org.springframework.web.servlet.view.InternalResourceViewResolver” > <property name = “prefix” value = “/ WEB-INF / jsp /” /> <property name = “suffix” value = “.jsp” /> </ bean> </ list> </ property> <property name = “defaultViews” > <list> <bean class = “org.springframework.web.servlet.view.json.MappingJackson2JsonView” /> </ list> </ property> </ bean>web.servlet.view.json.MappingJackson2JsonView“ /> </ list> </ property> </ bean>web.servlet.view.json.MappingJackson2JsonView“ /> </ list> </ property> </ bean> <bean id = “content” class = “com.foo.samples.rest.SampleContentAtomView” />该InternalResourceViewResolver手柄视图名称和JSP页面的翻译,而BeanNameViewResolver返回基于bean的名称的视图。(有关Spring如何查找和实例化视图的更多详细信息,请参阅“ 使用ViewResolver界面解析视图”。)在此示例中,该content bean是继承的类,该类AbstractAtomFeedView返回Atom RSS提要。有关创建Atom Feed表示的更多信息,请参阅Atom视图。
在上述配置中,如果使用扩展名进行请求.html,视图解析器将查找与text/html媒体类型匹配的视图。在 InternalResourceViewResolver提供了用于匹配视图text/html。如果请求是使用文件扩展名.atom,视图解析器将查找与application/atom+xml媒体类型相匹配的视图。该视图由该 BeanNameViewResolver映射提供给SampleContentAtomView如果返回的视图名称是content。如果使用文件扩展名进行请求.json,则无论视图名称如何, MappingJackson2JsonView将从DefaultViews列表中选择实例。或者,客户端请求可以在没有文件扩展名的情况下进行,但是将Accept标题设置为首选媒体类型,并且将发生对视图请求的相同解析。
如果“ContentNegotiatingViewResolver”的ViewResolver列表未被明确配置,它会自动使用应用程序上下文中定义的任何ViewResolvers。相应的控制器代码返回表单的URI http://localhost/content.atom或http://localhost/content应用Accept程序/ atom + xml 的头部的Atom RSS提要 如下所示。
@Controller public class ContentController { private List <SampleContent> contentList = new ArrayList <SampleContent>(); @GetMapping(“/ content”) public ModelAndView getContent(){ ModelAndView mav = new ModelAndView(); mav.setViewName(“content”); mav.addObject(“sampleContentList”,contentList); 返回 mav } }Flash属性为一个请求存储旨在用于另一个的属性提供了一种方法。这是重定向时最常用的 – 例如 Post / Redirect / Get模式。闪存属性在重定向(通常在会话中)之前临时保存,以便在重定向后立即对请求提供可用的请求。
Spring MVC有两个主要的抽象支持Flash属性。FlashMap用于保存Flash属性,FlashMapManager用于存储,检索和管理 FlashMap实例。
Flash属性支持始终是“开”的,不需要明确启用,尽管如果不使用它,它不会导致HTTP会话创建。在每个请求上都有一个“输入” FlashMap,它具有从先前的请求传递的属性(如果有的话)和FlashMap带有属性的“输出” ,以保存后续请求。这两个FlashMap实例可以通过静态方法在Spring MVC中从任何地方访问RequestContextUtils。
注释控制器通常不需要FlashMap直接使用。相反, @RequestMapping方法可以接受类型的参数,RedirectAttributes并使用它来为重定向方案添加闪存属性。添加的Flash属性将 RedirectAttributes自动传播到“输出”FlashMap。类似地,在重定向之后,来自“输入” FlashMap的属性将自动添加到 Model为目标URL提供服务的控制器中。
匹配请求到Flash属性
闪存属性的概念存在于许多其他Web框架中,并且已被证明有时暴露于并发问题。这是因为根据定义,flash属性将被存储直到下一个请求。然而,“下一个”请求可能不是预期的接收方,而是另一个异步请求(例如轮询或资源请求),在这种情况下,闪存属性过早地被删除。
为了减少此类问题的可能性,请使用目标重定向网址的路径和查询参数RedirectView自动“邮票” FlashMap实例。反过来,默认情况下FlashMapManager,当查询“输入”时,将该信息与传入请求相匹配FlashMap。
这不能完全消除并发问题的可能性,但是仍然可以通过重定向URL中已经提供的信息来大大减少它。因此,使用Flash属性主要用于重定向方案。
Spring MVC提供了一种用于使用UriComponentsBuilder和构建和编码URI的机制 UriComponents。
例如,您可以扩展和编码URI模板字符串:
UriComponents uriComponents = UriComponentsBuilder.fromUriString( “http://example.com/hotels/{hotel}/bookings/{booking}”).build(); URI uri = uriComponents.expand(“42”,“21”).encode()。toUri();请注意,这UriComponents是不可变的expand(),encode()如果需要,并且操作返回新的实例。
您还可以使用各个URI组件进行扩展和编码:
UriComponents uriComponents = UriComponentsBuilder.newInstance() .scheme(“http”). host(“example.com”).path(“/酒店/ {酒店} /预订/ {预订}”).build() .expand(“42”,“21”) .encode();在Servlet环境中,ServletUriComponentsBuilder子类提供静态工厂方法从Servlet请求中复制可用的URL信息:
HttpServletRequest request = ... //重新使用host,scheme,port,path和query string //替换“accountId”查询参数 ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromRequest(request) .replaceQueryParam(“accountId”,“{id}”).build() .expand(“123”) .encode();或者,您可以选择复制可用信息的子集,直到并包括上下文路径:
//重新使用主机,端口和上下文路径 //将“/ accounts”附加到路径 ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromContextPath(request) .path(“/ accounts”).build()或者在DispatcherServlet按名称(例如/main/*)映射的情况下,还可以包含servlet映射的文字部分:
//重新使用主机,端口,上下文路径 //将servlet映射的文字部分附加到路径 //将“/ accounts”附加到路径 ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromServletMapping(request) .path(“/ accounts”).build()Spring MVC还提供了一种构建控制器方法链接的机制。例如,给出:
@Controller @RequestMapping(“/ hotels / {hotel}”) public class BookingController { @GetMapping(“/ bookings / {booking}”) public String getBooking( @PathVariable Long booking){ // ... } }您可以通过名称参考方法来准备链接:
UriComponents uriComponents = MvcUriComponentsBuilder .fromMethodName(BookingController 类,“getBooking” ,21).buildAndExpand(42); URI uri = uriComponents.encode()。toUri();在上面的例子中,我们提供了实际的方法参数值,在这种情况下是长值21,用作路径变量并插入到URL中。此外,我们提供了值42,以填充任何剩余的URI变量,例如从类型级请求映射继承的“hotel”变量。如果该方法有更多的参数,您可以为URL不需要的参数提供null。一般而言@PathVariable,@RequestParam参数与构造URL相关。
还有其他的使用方法MvcUriComponentsBuilder。例如,您可以使用类似于通过代理模拟测试的技术,以避免通过名称引用控制器方法(示例假定静态导入MvcUriComponentsBuilder.on):
UriComponents uriComponents = MvcUriComponentsBuilder .fromMethodCall(上(BookingController。类).getBooking(21))buildAndExpand(42); URI uri = uriComponents.encode()。toUri();上面的例子使用静态方法MvcUriComponentsBuilder。在内部,他们依靠ServletUriComponentsBuilder从当前请求的方案,主机,端口,上下文路径和servlet路径准备基本URL。这在大多数情况下运行良好,但有时可能不足。例如,您可能不在请求的上下文中(例如,准备链接的批处理),或者您可能需要插入路径前缀(例如,从请求路径中删除并需要重新插入到链接中的区域设置前缀)。
对于这种情况,您可以使用接受a UriComponentsBuilder使用基本URL 的静态“fromXxx”重载方法 。或者您可以MvcUriComponentsBuilder 使用基本URL 创建一个实例,然后使用基于实例的“withXxx”方法。例如:
UriComponentsBuilder base = ServletUriComponentsBuilder.fromCurrentContextPath()。path(“/ en”); MvcUriComponentsBuilder builder = MvcUriComponentsBuilder.relativeTo(base); builder.withMethodCall(上(BookingController。类).getBooking(21))buildAndExpand(42); URI uri = uriComponents.encode()。toUri();您还可以从JSP,Thymeleaf,FreeMarker等视图中构建带注释控制器的链接。这可以使用fromMappingName方法,MvcUriComponentsBuilder 其中指的是按名称映射。
每个都会@RequestMapping根据类的大写字母和完整的方法名称分配一个默认名称。例如,getFoo类FooController 中的方法被分配名称“FC#getFoo”。该策略可以通过创建一个实例HandlerMethodMappingNamingStrategy并将其插入到您中 来进行替换或定制RequestMappingHandlerMapping。默认策略实现也会查看name属性@RequestMapping,如果存在,则使用该属性。这意味着如果分配的默认映射名称与另一个冲突(例如重载方法),则可以在该目录上明确指定一个名称@RequestMapping。
分配的请求映射名称在启动时记录在TRACE级别。Spring JSP标签库提供了一个名为mvcUrl该函数的函数,可用于根据此机制准备到控制器方法的链接。
例如:
@RequestMapping(“/ people / {id} / addresses”) public class PersonAddressController { @RequestMapping(“/ {country}”) public HttpEntity getAddress( @PathVariable String country){...} }您可以从JSP准备一个链接,如下所示:
%@ taglib uri =“http://www.springframework.org/tags”prefix =“s”%> ... <a href="${s:mvcUrl('PAC#getAddress').arg(0,'US').buildAndExpand('123')}">获取地址</a>上述示例依赖于mvcUrl在Spring标签库(即META-INF / spring.tld)中声明的JSP函数。对于更高级的案例(例如上一节所述的自定义基本URL),可以轻松地定义自己的函数或使用自定义标记文件,以便使用MvcUriComponentsBuilder具有自定义基本URL 的特定实例。
Spring的大部分架构支持国际化,就像Spring Web MVC框架一样。DispatcherServlet使您能够使用客户端的语言环境自动解析邮件。这是用LocaleResolver对象完成的。
当一个请求进来时,DispatcherServlet寻找一个区域设置解析器,如果它找到一个它试图使用它来设置区域设置。使用该RequestContext.getLocale() 方法,您可以随时检索由语言环境解析器解析的区域设置。
除了自动区域设置解析之外,您还可以将拦截器附加到处理程序映射(参见第18.4.1节“拦截与HandlerInterceptor的请求”以获取有关处理程序映射拦截器的更多信息),以在特定情况下更改区域设置,例如,基于请求中的参数。
org.springframework.web.servlet.i18n程序包中定义了语言环境解析器和拦截器, 并以正常方式在应用程序上下文中配置。以下是Spring中包含的区域解析器的选择。
除了获取客户的区域设置之外,了解他们的时区通常也是有用的。该LocaleContextResolver界面提供了一个扩展LocaleResolver,允许解析器提供更丰富LocaleContext,可能包括时区信息。
如果可用,TimeZone可以使用该RequestContext.getTimeZone()方法获得 用户。时区信息将自动被Date / Time Converter和FormatterSpring注册的对象使用ConversionService。
该语言环境解析器检查accept-language客户端发送的请求中的标题(例如,Web浏览器)。通常此标题字段包含客户端操作系统的区域设置。请注意,此解析器不支持时区信息。
这个本地化解析器检查一个Cookie可能的客户端中,看是否有 Locale或TimeZone指定。如果是这样,它使用指定的细节。使用此语言环境解析器的属性,您可以指定cookie的名称以及最大的年龄。在下面找到一个定义a的例子CookieLocaleResolver。
<bean id = “localeResolver” class = “org.springframework.web.servlet.i18n.CookieLocaleResolver” > <property name = “cookieName” value = “clientlanguage” /> <! - 以秒为单位。如果设置为-1,则cookie不会持久化(在浏览器关闭时删除) - > <property name = “cookieMaxAge” value = “100000” /> </豆>表18.4。CookieLocaleResolver属性
属性默认描述cookieNameclassname + LOCALEcookie的名称名cookieMaxAgeServlet容器默认Cookie在客户端上保持持续的最长时间。如果指定了-1,则cookie不会被持久化; 只有客户端关闭浏览器才可用。cookiePath/将Cookie的可见性限制在您网站的某个部分。指定cookiePath时,cookie将只对该路径及其下方的路径可见。在SessionLocaleResolver可以检索Locale并TimeZone从可能与用户的请求相关的会话。相反 CookieLocaleResolver,该策略将本地选择的区域设置存储在Servlet容器中HttpSession。因此,这些设置对于每个会话都是临时的,因此在每个会话终止时丢失。
请注意,与Spring Session项目之类的外部会话管理机制没有直接关系。这SessionLocaleResolver将简单地根据HttpSession当前值来评估和修改相应的属性HttpServletRequest。