4# 混合使用多种视图技术。
在前面文章里,我们对jsp、json、xml个中视图都进行了较为详细的实例解析,但涉及到的都是单视图使用配置。在实际开发中,我们可能需要混合是使用多种视图技术。尤其是针对REST编程风格,我们可以通过一个URL、多种视图来切合REST风格的同一资源、多种表述。
现在加入我们要输出JSP、JSON、XML多种视图技术,如果使用我之前文章《springMVC4(4)json与对象互转实例解析请求响应数据转换器 》提到的HttpMessageConvert来完成数据类型输出切换。它相对于多视图输出的局限性是:
1. 必须通过HTTP请求头的Accept来控制转换器的使用类型,如果客户端是安卓等还能通过HttpClient、RestTemplate等控制,但如果客户端是游览器,除非使用AJAX技术,否则很难控制请求头内容
2. 无法通过URL扩展名或请求参数来控制服务端的资源输出类型。而使用多种视图技术,我们可以通过以下形式控制输出不同视图:
1. 扩展名:
1. /user.xml 呈现xml文件
2. /user.json 呈现json格式
3. /user.xls 呈现excel文件
4. /user.pdf 呈现pdf文件
5. /user 使用默认view呈现,比如jsp等
2. 请求参数:
1. /user?type=xml 呈现xml文件
2. /user?type=json 呈现json格式
3. /user?type=xls 呈现excel文件
4. /user?type=pdf 呈现pdf文件
5. /user? 使用默认view呈现,比如jsp等
ContentNegotiatingViewResolver
我们使用ContentNegotiatingViewResolver视图解析器来完成多种视图混合解析,从它的名字上看,它是一个视图协调器,负责根据请求信息从当前环境选择一个最合适的解析器进行解析,也即是说,它本身并不负责解析视图。
它有3个关键属性:
1. favorPathExtension:如果设置为true(默认为true),则根据URL中的文件拓展名来确定MIME类型
2. favorPathExtension:如果设置为true(默认为false),可以指定一个请求参数确定MIME类型,默认的请求参数为format,可以通过parameterName属性指定一个自定义属性。
3. ignoreAcceptHeader(默认为false),则采用Accept请求报文头的值确定MIME类型。由于不同游览器产生的Accept头不一致,不建议采用Accept确定MIME类型。
在实际流程中,ContentNegotiatingViewResolver也是根据以上三个互斥属性的配置情况来确定视图类型,其中属性1优先级最高,属性3优先级最低
除了以上三个属性,还有一个关键属性是mediaTypes,用来配置不同拓展名或参数值映射到不同的MIME类型
在前面,我们展示了使用jsp/模板、json、xml、Excel等来呈现我们的视图,下面我们通过整合上述视图来分析我们的多视图混合技术。
多视图混合Rest呈现实例
1. 配置视图解析器
<!-- 根据确定出的不同MIME名,使用不同视图解析器解析视图 -->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<!-- 设置优先级 -->
<property name="order" value="1" />
<!-- 设置默认的MIME类型,如果没有指定拓展名或请求参数,则使用此默认MIME类型解析视图 -->
<property name="defaultContentType" value="text/html" />
<!-- 是否不适用请求头确定MIME类型 -->
<property name="ignoreAcceptHeader" value="true" />
<!-- 是否根据路径拓展名确定MIME类型 -->
<property name="favorPathExtension" value="false" />
<!-- 是否使用参数来确定MIME类型 -->
<property name="favorParameter" value="true" />
<!-- 上一个属性配置为true,我们指定type请求参数判断MIME类型 -->
<property name="parameterName" value="type" />
<!-- 根据请求参数或拓展名映射到相应的MIME类型 -->
<property name="mediaTypes">
<map>
<entry key="html" value="text/html" />
<entry key="xml" value="application/xml" />
<entry key="json" value="application/json" />
<entry key="excel" value="application/vnd.ms-excel"></entry>
</map>
</property>
<!-- 设置默认的候选视图,如果有合适的MIME类型,将优先从以下选择视图,找不到再在整个Spring容器里寻找已注册的合适视图 -->
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.InternalResourceView">
<property name="url" value="WEB-INF/views/hello.jsp"></property>
</bean>
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
<ref local="myXmlView" />
<bean class="com.mvc.view.ExcelView" />
</list>
</property>
</bean>
<!-- Excel视图 -->
<bean class="com.mvc.view.ExcelView" id="excelView" /><!-- 注册自定义视图 -->
<bean class="org.springframework.web.servlet.view.xml.MarshallingView"
id="myXmlView">
<property name="modelKey" value="articles" />
<property name="marshaller" ref="xmlMarshaller" />
</bean>
<bean class="org.springframework.oxm.xstream.XStreamMarshaller"
id="xmlMarshaller"><!-- 将模型数据转换为XML格式 -->
<property name="streamDriver">
<bean class="com.thoughtworks.xstream.io.xml.StaxDriver" />
</property>
</bean>
关于以上视图文件的配置实体讲解可移步参考我前面的文章
2. jsp视图文件
<%@page import="com.mvc.model.Article"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>hello spring mvc</title>
</head>
<body>
<c:out value="${articles}"></c:out>
</body>
</html>
3. Excel配置文件
package com.mvc.view;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.web.servlet.view.document.AbstractExcelView;
import com.mvc.model.Article;
public class ExcelView extends AbstractExcelView {
@Override
protected void buildExcelDocument(Map<String, Object> model,
HSSFWorkbook workbook, HttpServletRequest request,
HttpServletResponse response) throws Exception {
List<Article> articles= (List<Article>) model.get("articles");
HSSFSheet sheet = workbook.createSheet("文章列表");//创建一页
HSSFRow header = sheet.createRow(0);//创建第一行
header.createCell(0).setCellValue("标题");
header.createCell(1).setCellValue("正文");
for( int i = 0; i < articles.size();i++){
HSSFRow row = sheet.createRow(i + 1);
Article article = articles.get(i);
row.createCell(0).setCellValue(article.getTitle());
row.createCell(1).setCellValue(article.getContent());
}
}
}
4. Article POJO类
package com.mvc.model;
public class Article {
private String title;
private String content;
//忽略get和set方法
@Override
public String toString() {
return "Article [ title=" + title + ", content="
+ content + "]";
}
}
5. 控制器测试方法
@RequestMapping("views")
public String views(ModelMap map,HttpServletRequest request){
List<Article>articles = new ArrayList<Article>();
for(int i = 0 ; i < 5; i ++){
Article article = new Article();
article.setTitle("title" +i);
article.setContent("content" + i);
articles.add(article);
}
map.addAttribute("articles",articles);//将文章对象绑定到
return "views";
}
6. 进行测试
我们使用了参数type来映射不同的视图类型:
1. 默认参数类型:
2. html参数类型
3. json参数类型
4. xml参数类型
5. excel参数类型
点击下载后打开如下图所示:
7. 使用拓展名类型
上面我们使用参数的方法访问,如果我们改成使用拓展名的形式,如下所示,只需去掉其中的4行配置:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"><!-- 根据确定出的不同MIME名,使用不同视图解析器解析视图 -->
<property name="order" value="1" /><!-- 设置优先级 -->
<property name="defaultContentType" value="text/html" /><!-- 设置默认的MIME类型,如果没有指定拓展名或请求参数,则使用此默认MIME类型解析视图 -->
<property name="mediaTypes"><!-- 根据请求参数映射到相应的MIME类型 -->
<map>
<entry key="html" value="text/html" />
<entry key="xml" value="application/xml" />
<entry key="json" value="application/json" />
<entry key="excel" value="application/vnd.ms-excel"></entry>
</map>
</property>
<property name="defaultViews"><!-- 设置默认的候选视图,如果有合适的MIME类型,将优先从以下选择视图,找不到再在整个Spring容器里寻找已注册的合适视图 -->
<list>
<bean class="org.springframework.web.servlet.view.InternalResourceView">
<property name="url" value="WEB-INF/views/hello.jsp"></property>
</bean>
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
<ref local="myXmlView" />
<bean class="com.mvc.view.ExcelView" />
</list>
</property>
</bean>
这个时候,我们就可以使用:
http://localhost:8080/springMVC/views
http://localhost:8080/springMVC/views.html,
http://localhost:8080/springMVC/views.json,
http://localhost:8080/springMVC/views.xml
来对应得到与上面相同的内容。感兴趣的朋友可在下面下载源码自行测试
源码下载
本节内容源码可到http://github.com/jeanhao/spring的multiViews文件夹下下载