使用 Struts 2 开发 RESTful 服务Word文件下载.docx
- 文档编号:18672998
- 上传时间:2022-12-31
- 格式:DOCX
- 页数:20
- 大小:164.83KB
使用 Struts 2 开发 RESTful 服务Word文件下载.docx
《使用 Struts 2 开发 RESTful 服务Word文件下载.docx》由会员分享,可在线阅读,更多相关《使用 Struts 2 开发 RESTful 服务Word文件下载.docx(20页珍藏版)》请在冰豆网上搜索。
对于一个需要访问该系统资源的第三方系统而言,同样无法明白这个系统包含多少功能和信息。
URI和URL
与URI相关的概念还有URL,URL是UniformResourceLocator,也就是统一资源定位符的意思。
其中http:
//www.crazyit.org就是一个统一资源定位符,URL是URI的子集。
简而言之:
每个URL都是URI,但不是每个URI都是URL。
从REST架构的角度来看,该系统里包含的所有功能和信息,都可被称为资源(Resource),REST架构中的资源包含静态页面、JSP和Servlet等,该应用暴露在网络上的所有功能和信息都可被称为资源。
除此之外,REST架构规范了应用资源的命名方式,REST规定对应用资源使用统一的命名方式:
REST系统中的资源必须统一命名和规划,REST系统由使用URI(UniformResourceIdentifier,即统一资源标识符)命名的资源组成。
由于REST对资源使用了基于URI的统一命名,因此这些信息就自然地暴露出来了,从而避免“信息地窖”的不良后果。
对于当今最常见的网络应用来说,资源标识符就是URI,资源的使用者则根据URI来操作应用资源。
当URI发生改变时,表明客户机所使用的资源发生了改变。
从资源的角度来看,当客户机操作不同的资源时,资源所在的Web页(将Web页当成虚拟的状态机来看)的状态就会发生改变、迁移(Transfer),这就是REST术语中ST(StateTranfer)的由来了。
客户机为了操作不同状态的资源,则需要发送一些Representational的数据,这些数据包含必要的交互数据,以及描述这些数据的元数据。
这就是REST术语中RE(Representational)的由来了。
理解了这个层次之后,至于REST如何翻译、或是否真正给它一个中文术语,读者可自行决定。
操作资源的方式
对于REST架构的服务器端而言,它提供的是资源,但同一资源具有多种表现形式(可通过在HTTPContent-type头中包含关于数据类型的元数据)。
如果客户程序完全支持HTTP应用协议,并能正确处理REST架构的标准数据格式,那么它就可以与世界上任意一个REST风格的用户交互。
这种情况不仅适用于从服务器端到客户端的数据,反之亦然——倘若从客户端传来的数据符合REST架构的标准数据格式,那么服务器端也可以正确处理数据,而不去关心客户端的类型。
典型情况下,REST风格的资源能以XHTML、XML和JSON三种形式存在,其中XML格式的数据是WebServices技术的数据交换格式,而JSON则是另一种轻量级的数据交换格式;
至于XHTML格式则主要由浏览器负责呈现。
当服务器为所有资源提供多种表现形式之后,这些资源不仅可以被标准Web浏览器所使用,还可以由JavaScript通过Ajax技术调用,或者以RPC(RemoteProcedureCall)风格调用,从而变成REST风格的WebServices。
REST架构除了规定服务器提供资源的方式之外,还推荐客户端使用HTTP作为GenericConnectorInterface(也就是通用连接器接口),而HTTP则把对一个URI的操作限制在了4个之内:
GET、POST、PUT和DELETE。
通过使用通用连接器接口对资源进行操作的好处是保证系统提供的服务都是高度解耦的,从而简化了系统开发,改善了系统的交互性和可重用性。
REST架构要求客户端的所有的操作在本质上是无状态的,即从客户到服务器的每个Request都必须包含理解该Request的所有必需信息。
这种无状态性的规范提供了如下几点好处:
∙无状态性使得客户端和服务器端不必保存对方的详细信息,服务器只需要处理当前Request,而不必了解前面Request的历史。
∙无状态性减少了服务器从局部错误中恢复的任务量,可以非常方便地实现FailOver技术,从而很容易地将服务器组件部署在集群内。
∙无状态性使得服务器端不必在多个Request中保存状态,从而可以更容易地释放资源。
∙无状态性无需服务组件保存Request状态,因此可让服务器充分利用Pool技术来提高稳定性和性能。
当然,无状态性会使得服务器不再保存Request的状态数据,因此需要在一系列Request中发送重复数据,从而提高了系统的通信成本。
为了改善无状态性带来的性能下降,REST架构填加了缓存约束。
缓存约束允许隐式或显式地标记一个Response中的数据,这样就赋予了客户端缓存Response数据的功能,这样就可以为以后的Request共用缓存的数据,部分或全部的消除一些交互,增加了网络的效率。
但是用于客户端缓存了信息,也就同时增加了客户端与服务器数据不一致的可能,从而降低了可靠性。
Struts2的REST支持
约定优于配置
Convention这个单词的翻译过来就是“约定”的意思。
有RubyOnRails开发经验的读者知道Rails有一条重要原则:
约定优于配置。
Rails开发者只需要按约定开发ActiveRecord、ActiveController即可,无需进行配置。
很明显,Struts2的Convention插件借鉴了Rails的创意,甚至连插件的名称都借鉴了“约定优于配置”原则。
从Struts2.1开始,Struts2改为使用Convention插件来支持零配置。
Convention插件彻底地抛弃了配置信息,不仅不需要使用struts.xml文件进行配置,甚至不需要使用Annotation进行配置。
而是由Struts2根据约定来自动配置。
由于Struts2的Convention插件的主要特点是“约定优于配置”,当我们已经习惯了Struts2的基本开发方法之后,如果希望改为使用Convention插件也非常容易,我们只要放弃Stuts2.1应用原有的配置文件,改为按Convention插件的约定来定义Action即可。
以Convention插件为基础,Struts2.1又新增了REST插件,允许Struts2应用对外提供REST服务。
REST插件也无需使用XML进行配置管理。
Struts2.1通过REST插件完全可以提供让人和机器客户端共同使用的资源,并支持RubyOnRails风格的URL。
RestActionMapper简介
从本质上来看,Struts2依然是一个MVC框架,最初设计Struts2时并没有按REST架构进行设计,因此Struts2本质上并不是一个REST框架。
由于Struts2提供了良好的可扩展性,因此允许通过REST插件将其扩展成支持REST的框架。
REST插件的核心是RestActionMapper,它负责将Rails风格的URL转换为传统请求的URL。
用WinRAR打开struts2-rest-plugin-2.1.6文件,看到该文件里包含一个struts-plugin.xml文件,该文件中包含如下一行:
<
!
--定义支持REST的ActionMapper-->
beantype="
org.apache.struts2.dispatcher.mapper.ActionMapper"
name="
rest"
class="
org.apache.struts2.rest.RestActionMapper"
/>
通过查看RestActionMapper的API说明,我们发现它可接受如下几个参数:
∙struts.mapper.idParameterName:
用于设置ID请求参数的参数名,该属性值默认是id。
∙struts.mapper.indexMethodName:
设置不带id请求参数的GET请求调用Action的哪个方法。
该属性值默认是index。
∙struts.mapper.getMethodName:
设置带id请求参数的GET请求调用Action的哪个方法。
该属性值默认是show。
∙struts.mapper.postMethodName:
设置不带id请求参数的POST请求调用Action的哪个方法。
该属性值默认是create。
∙struts.mapper.putMethodName:
设置带id请求参数的PUT请求调用Action的哪个方法。
该属性值默认是update。
∙struts.mapper.deleteMethodName:
设置带id请求参数的DELETE请求调用Action的哪个方法。
该属性值默认是destroy。
∙struts.mapper.editMethodName:
设置带id请求参数、且指定操作edit资源的GET请求调用Action的哪个方法。
该属性值默认是edit。
∙struts.mapper.newMethodName:
设置不带id请求参数、且指定操作edit资源的GET请求调用Action的哪个方法。
该属性值默认是editNew。
在RestActionMapper的方法列表中,我们看到setIdParameterName、setIndexMethodName、setGetMethodName、setPostMethodName、setPutMethodName、setDeleteMethodName、setEditMethodName、setNewMethodName等方法,这些方法对应为上面列出的方法提供setter支持。
通常情况下,我们没有必要改变RestActionMapper的参数,直接使用这些参数的默认值就可支持Rails风格的REST。
根据前面介绍可以看出:
支持REST风格的Action至少包含如下7个方法:
∙index:
处理不带id请求参数的GET请求。
∙show:
处理带id请求参数的GET请求。
∙create:
处理不带id请求参数的POST请求。
∙update:
处理带id请求参数的PUT请求。
∙destroy:
处理带id请求参数的DELETE请求。
∙edit:
处理带id请求参数,且指定操作edit资源的GET请求。
∙editNew:
处理不带id请求参数,且指定操作edit资源的GET请求。
如果请求需要向服务器发送id请求参数,直接将请求参数的值附加在URL中即可。
表1显示了RestActionMapper对不同HTTP请求的处理结果。
表1.RestActionMapper对HTTP请求的处理
HTTP方法
URI
调用Action的方法
请求参数
GET
/book
index
POST
create
PUT
/book/2
update
id=2
DELETE
destroy
show
/book/2/edit
edit
/book/new
editNew
不幸地是,标准HTML语言目前根本不支持PUT和DELETE两个操作,为了弥补这种不足,REST插件允许开发者提交请求时额外增加一个_method请求参数,该参数值可以为PUT或DELETE,用于模拟HTTP协议的PUT和DELETE操作。
为Struts2应用安装REST插件
安装REST插件非常简单,只需按如下步骤进行即可:
1.将Struts2项目下struts2-convention-plugin-2.1.6.jar、struts2-rest-plugin-2.1.6.jar两个JAR包复制到Web应用的WEB-INF\lib路径下。
2.由于Struts2的REST插件还需要将提供XML、JSON格式的数据,因此还需要将xstream-1.2.2.jar、json-lib-2.1.jar、ezmorph-1.0.3.jar以及Jakarta-Common相关JAR包复制到Web应用的WEB-INF/lib路径下。
3.通过struts.xml、struts.properties或web.xml改变struts.convention.default.parent.package常量的值,让支持REST风格的Action所在的包默认继承rest-default,而不是继承默认的convention-default父包。
对于第三个步骤而言,开发者完全可以不设置该常量,如果开发者不设置该常量,则意味着开发者必须通过Annotation为每个Action类设置父包。
实现支持REST的Action类
在实现支持REST的Action之前,我们先为系统提供一个Model类:
Book,该Book类非常简单,代码如下:
publicclassBook
{
privateIntegerid;
privateStringname;
privatedoubleprice;
//无参数的构造器
publicBook(){}
//id属性的setter和getter方法
publicvoidsetId(Integerid)
{
this.id=id;
}
publicIntegergetId()
returnthis.id;
//省略name和price的setter和getter方法
...
}
除了提供上面的Book类之外,我们还为该Book类提供一个业务逻辑组件:
BookService。
为了简单起见,BookService类不再依赖DAO组件访问数据库,而是直接操作内存中的Book数组——简单地说,本系统中状态是瞬态的,没有持久化保存,应用运行过程中这些状态一直存在,但一旦重启该应用,则系统状态丢失。
下面是BookService类的代码:
publicclassBookService
privatestaticMap<
Integer,Book>
books
=newHashMap<
();
//保留下本图书的ID
privatestaticintnextId=5;
//以内存中的数据模拟数据库的持久存储
static{
books.put(1,newBook(1
"
疯狂Java讲义"
99));
books.put(2,newBook(2
轻量级JavaEE企业应用实战"
89));
books.put(3,newBook(3
疯狂Ajax讲义"
78));
books.put(4,newBook(4
Struts2权威指南"
79));
//根据ID获取
publicBookget(intid)
returnbooks.get(id);
//获取系统中全部图书
publicList<
Book>
getAll()
returnnewArrayList<
(books.values());
//更新已有的图书或保存新图书
publicvoidsaveOrUpdate(Bookbook)
//如果试图保存的图书的ID为null,表明是保存新的图书
if(book.getId()==null)
//为新的图书分配ID。
book.setId(nextId++);
//将保存book
books.put(book.getId(),book);
//删除图书
publicvoidremove(intid)
books.remove(id);
从上面粗体字代码可以看出,BookService提供了4个方法,用于实现对Book对象的CRUD操作。
下面开始定义支持REST的Action类了,这个Action类与前面介绍Struts2的普通Action存在一些差异——因为该Action不再用execute()方法来处理用户请求,而是使用前面介绍的7个标准方法来处理用户请求。
除此之外,该Action总是需要处理id请求参数,因此必须提供id请求参数,并为之提供对应的setter和getter方法。
因为本系统已经提供了BookModel类,并且为了更好的模拟Rails中ActiveController(Controller)直接访问ActiveRecord(Model)的方式,本系统采用了ModelDriven的开发方式,下面是本系统中支持REST的Action类的代码。
//定义返回success时重定向到bookAction
@Results(@Result(name="
success"
type="
redirectAction"
params={"
actionName"
book"
}))
publicclassBookControllerextendsActionSupport
implementsModelDriven<
Object>
//封装id请求参数的属性
privateintid;
privateBookmodel=newBook();
privateList<
list;
//定义业务逻辑组件
privateBookServicebookService=newBookService();
//获取id请求参数的方法
publicvoidsetId(intid)
//取得方法时顺带初始化model对象
if(id>
0)
this.model=bookService.get(id);
publicintgetId()
//处理不带id参数的GET请求
//进入首页
publicHttpHeadersindex()
list=bookService.getAll();
returnnewDefaultHttpHeaders("
index"
)
.disableCaching();
//进入添加新图书。
publicStringeditNew()
//创建一个新图书
model=newBook();
return"
editNew"
;
//处理不带id参数的POST请求
//保存新图书
publicHttpHeaderscreate()
//保存图书
bookService.saveOrUpdate(model);
addActionMessage("
添加图书成功"
);
returnnewDefaultH
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 使用 Struts 开发 RESTful 服务