此REST插件简化了人们和电脑客户端对REST风格资源的访问。其目标是100%实现和Ruby on Rails REST URL风格相兼容的,并且可以免除使用XML作为资源的开发模式。它基于Convention Plugin来支持对action和result的自动配置。

此Rest风格的action映射强制兼容Ruby-On-Rails的风格映射。如果方法未被指定(通过“!”或者“method:”前缀),那么将基于ReST风格的约定(通过检查URL和HTTP方法)进行猜测。这里特别说明的是,此插件和codebehind plugin一起也可以正常工作,因此,不再需要使用基于XML的配置文件。

此映射支持如下参数:

struts.mapper.idParameterName - 如果设置,则它的值将被作为保存id的参数名,然后id将被从action名称中移除。不管是否指定了相关方法,此映射都会试图从url中移除标识符并将它作为一个参数保存。
struts.mapper.indexMethodName - 在没有id参数的情况下,通过GET请求调用的方法名。默认为index。
struts.mapper.getMethodName - 在具有id参数的情况下,通过GET请求调用的方法名。默认为show。
struts.mapper.postMethodName - 在没有id参数的情况下,通过POST请求调用的方法名。默认为create。
struts.mapper.putMethodName - 在具有id参数的情况下,通过PUT请求调用的方法名。默认为update。
struts.mapper.deleteMethodName - 在具有id参数的情况下,通过DELETE请求调用的方法名。默认为destory。
struts.mapper.editMethodName - 在具有id参数且指定了“edit”视图的情况下,通过GET请求调用的方法名。默认为edit。
struts.mapper.newMethodName - 在没有id参数且指定了“new”视图的情况下,通过GET请求调用的方法名。默认为editNew。


下列URL将调用Action的相关方法如下:

GET: /movies => method="index" 
 
GET: /movies/Thrillers => method="show", id="Thrillers" 
 
GET: /movies/Thrillers;edit => method="edit", id="Thrillers" 
 
GET: /movies/Thrillers/edit => method="edit", id="Thrillers" 
 
GET: /movies/new => method="editNew" 
 
POST: /movies => method="create" 
 
PUT: /movies/Thrillers => method="update", id="Thrillers" 
 
DELETE: /movies/Thrillers => method="destroy", id="Thrillers"


为了模拟HTTP方法的 PUT和DELETE请求,因为HTML并不支持,我们将使用一个叫做“_method”的HTTP参数。

或者,使用下表的表示方式:

HTTP method URI Class.method parameters 
 
GET /movie Movie.index 
 
POST /movie Movie.create 
 
PUT /movie/Thrillers Movie.update id="Thrillers" 
 
DELETE /movie/Thrillers Movie.destroy id="Thrillers" 
 
GET /movie/Thrillers Movie.show id="Thrillers" 
 
GET /movie/Thrillers/edit Movie.edit id="Thrillers" 
 
GET /movie/new Movie.editNew



除了作为一个REST风格的URL映射器以外,此插件还对于multiple content types、通过URL扩展名的可切换性提供了内建的支持。这样,一个单一的资源就能够被作为一个multiple content types暴露出去,而不需要其他额外的工作。

例如,通过暴露一个“orders”资源,客户端可以立即通过如下方式进行访问:

http://my.company.com/myapp/orders/1 http://my.company.com/myapp/orders/1.xml
http://my.company.com/myapp/orders/1.xhtml
http://my.company.com/myapp/orders/1.json

此REST插件自动处理序列号和反序列化以及相应的格式化。

一、特性
完全实现 Ruby on Rails 的REST风格URL
支持免XML开发,不需要注解
内建序列号和反序列化支持,以支持XML和JSON
自动错误处理
针对HTTP响应的类型安全设置
自动有条件的GET支持

二、用法
在配置的包(package)中创建以“Controller”结尾的Java对象。这里的 “Controller”后缀用于区别REST action和普通的Struts2 action。尽管这完全是可选的,以及它们的功能完全相同。现在,加入方法即可处理各种请求。举例说明,下面的资源action将支持使用GET和 PUT请求访问/orders/34资源:

package org.apache.struts2.rest .example; 
 

public class OrdersController implements ModelDriven { 
 

    private OrderManager orderManager; 
 
    private String id; 
 
    private Order model; 
 

    // Handles /orders/{id} GET requests 
 
    public HttpHeaders show() { 
 
        model = orderManager.findOrder(id); 
 
        return new DefaultHttpHeaders("show" ) 
 
            .withETag(model.getUniqueStamp()) 
 
            .lastModified(model.getLastModified()); 
 
    } 
 

    // Handles /orders/{id} PUT requests 
 
    public String update() { 
 
        orderManager.updateOrder(model); 
 
        return "update" ; 
 
    } 
 

    // getters and setters 
 
}



在这个例子中,使用了ModelDriven 接口,来确保只有模型——这里的Order对象,被返回给客户端。否则,OrdersController 对象将被整个序列化。

你也许在想为什么show()方法返回了一个HttpHeaders 对象,而update()方法返回了预期的结果代码字符串。REST插件添加了对action方法的支持,对于那些通过响应有更多控制的action,返回HttpHeaders 对象是一种方式。在本例中,我们想要确保响应包括ETag 头和一个最近修改日期,以便信息可以被客户端正确的缓存。HttpHeaders 对象是一个方便的方式,可以在保证在类型安全的情况下控制响应。

而且,请注意,我们没有在上述任何一个方法中,返回通常的“success”结果码。这允许我们使用Codebehind Plugin插件的特性,当访问扩展名为.xhtml的资源时,以更加直观的方式来选择结果模板去处理。在本例中,我们可以通过为相应的方法创建 /orders-show.jsp 和/orders-update.jsp页面,来提供一个定制化的XHTML 资源视图。

2.1 定制ContentTypeHandlers
如果你需要处理不被默认处理器支持的扩展名时,你可以创建你自己的ContentTypeHandler 实现,并在struts.xml中进行定义即可。

<bean name="yaml" 
 
type="org.apache.struts2.rest.handler.ContentTypeHandler" 
 
class="com.mycompany.MyYamlContentHandler" 
 
/>



如果内建的内容类型处理器无法满足你的需要,那么你可以提供另一个处理器,覆盖对任何扩展名的默认处理方式,并使用它自己的别名进行声明,例如:

<bean name="myXml" type="org.apache.struts2.rest.handler.ContentTypeHandler" class="com.mycompany.MyXmlContentHandler" /> 

然后,只需要告知REST插件使用你自己的处理器,去覆盖特定扩展的处理器即可。在struts.properties文件中,可以做如下配置:

struts.rest.handlerOverride.xml=myXml 
 

2.2 关于struts.xml 
 
配置Struts以使用REST action映射器: 
 

<constant name="struts.mapper.class" 
 
value="rest" 
 
/>




因为REST插件使用Convention plugin,需要在Struts.xml中进行相关设置:

<constant name="struts.convention.action.suffix" 
 
value="Controller" 
 
/> 
 


<constant name="struts.convention.action.mapAllMatches" 
 
value="true" 
 
/> 
 


<constant name="struts.convention.default.parent.package" 
 
value="rest-default" 
 
/>




对于上述例子而言,package设置如下:

<constant name="struts.convention.package.locators" value="example" />

三、示例
此插件是随struts2-rest-showcase 应用程序一起发布的,用以演示一个简单的REST web应用程序。

该示例和不同于一般,控制器将被映射到一个关联的HTTP(PUT,DELETE)方法。

看下图可以让你更容易的理解REST的工作方式:

四、配置
配置可以定制,具体可以查看开发指南文档。要获得更多的配置项信息,可以查看 Convention Plugin的文档。
设置 描述 默认 可能值
struts.rest.handlerOverride.EXTENSION 处理EXTENSION 值的ContentTypeHandler 实现的别名 N/A 任何声明了的ContentTypeHandler实现的别名
struts.rest.defaultExtension 当没有在request中显示指定的时候默认使用扩展类型 xml 任意扩展名
struts.rest.validationFailureStatusCode 校验失败后返回的HTTP状态码 400 任意HTTP的数字状态码

五、安装
复制插件的jar包到应用程序的/WEB-INF/lib 即可。此插件依赖于Convention Plugin,因此,如果没有类似于Maven2那样的支持传递依赖的构建系统的话,你可能也需要加入Convention Plugin的jar包。

六、资源
http://www.b-simple.de/documents - 精短的RESTful Rails 教程(PDF格式, 有多种语言)
RESTful Web Services - 来自 O'Reilly 的一本书



2010 - 07 - 22



缩略显示



Struts2 之convention-plugin实现零配置[转]


文章分类:Java编程


最近开始关注struts2的新特性,从这个版本开始,Struts开始使用 convention-plugin代替codebehind-plugin来实现struts的零配置。
配置文件精简了,的确是简便了开发过程,但是,我们熟悉的配置突然disappear了,真是一下很不适应。跟着潮流走吧,看看该怎样来搞定 convention-plugin。
使用Convention插件,你需要将其JAR文件放到你应用的WEB-INF/lib目录中,你也可以在你Maven项目的POM文件中添加下面包依赖
Xml代码

1. <dependency>  
  
   2.   <groupId>org.apache.struts</groupId>  
  
   3.   <artifactId>struts2-convention-plugin</artifactId>  
  
   4.   <version>2.1.6</version>  
  
   5. </dependency>  
  

<dependency> 
  
  <groupId>org.apache.struts</groupId> 
  
  <artifactId>struts2-convention-plugin</artifactId> 
  
  <version>2.1.6</version> 
  
</dependency>




零配置并不是没有配置,而是通过约定大于配置的方式,大量通过约定来调度页面的跳转而使得配置大大减少。所以,首先应该了解下convention- plugin的约定:
1. 默认所有的结果页面都存储在WEB-INF/content下,你可以通过设置struts.convention.result.path这个属性的值来改变到其他路径。如:
Xml代码

1. <constant name="struts.convention.result.path" value="/WEB-INF/page" />  
  

<constant name="struts.convention.result.path" value="/WEB-INF/page" />




则将路径配置到了WEB-INF/page 下。
2. 默认包路径包含action,actions,struts,struts2的所有包都会被struts作为含有Action类的路径来搜索。你可以通过设置struts.convention.package.locators属性来修改这个配置。如:
Xml代码 复制代码

1. <constant name="struts.convention.package.locators" value="web,action" />  
  

<constant name="struts.convention.package.locators" value="web,action" />




则定义了在项目中,包路径包含web和action的将被视为Action存在的路径来进行搜索。
Com.ustb.web.*/com.ustb.action.*都将被视为含有Action的包路径而被搜索。
3. 接着,Convention从前一步找到的package以及其子package中寻找 com.opensymphony.xwork2.Action 的实现以及以Action结尾的类:
Java代码 复制代码

1. com.example.actions.MainAction   
  
   2. com.example.actions.products.Display (implements com.opensymphony.xwork2.Action)   
  
   3. com.example.struts.company.details.ShowCompanyDetailsAction  
  

com.example.actions.MainAction 
  
com.example.actions.products.Display (implements com.opensymphony.xwork2.Action) 
  
com.example.struts.company.details.ShowCompanyDetailsAction



4. 命名空间。从定义的.package.locators标示开始到包结束的部分,就是命名空间。举个例子:
Com.ustb.web.user.userAction的命名空间是:”/user”。 Com.ustb.web.user.detail.UserAction的命名空间是:”/user/detail”
5. Convention通过如下规则确定URL的具体资源部分:去掉类名的Action部分。然后将将每个分部的首字母转为小写,用’-’分割,你可以设置 struts.convention.action.name.separator 如
Xml代码 复制代码

1. <constant name="struts.convention.action.name.separator" value="-" />  
  

<constant name="struts.convention.action.name.separator" value="-" />




还是举个例子:
UserAction->user UserDetailAction ->user-detail。结合上面的。对于com.ustb.web.user.detail.UserDetailAction,映射的 url就是/WEB-INF/content/user/detail/user-detail.jsp
6. struts支持.jsp .html .htm .vm格式的文件。
下面是actiong和结果模版的映射关系:

URL Result 
  
File that could match Result Type 
  
/hello success /WEB-INF/content/hello.jsp Dispatcher 
  
/hello success /WEB-INF/content/hello-success.htm Dispatcher 
  
/hello success /WEB-INF/content/hello.ftl FreeMarker 
  
/hello-world input /WEB-INF/content/hello-world-input.vm Velocity 
  
/test1/test2/hello error /WEB-INF/content/test/test2/hello-error.html Dispatcher 
  

以上的内容来自struts2的文档http://struts.apache.org/2.1.6/docs/convention-plugin.html




当然,简单的通过默认的方式来进行配置不能完全满足实际项目的需要。所幸,convention的零配置是非常灵活的。
通过@Action注释
对如下例子:
Java代码 复制代码

1. package com.example.web;   
  
   2.   
  
   3. import com.opensymphony.xwork2.Action;   
  
   4. import com.opensymphony.xwork2.ActionSupport;   
  
   5.   
  
   6. public class HelloAction extends ActionSupport {   
  
   7.     @Action("action1")   
  
   8.     public String method1() {   
  
   9.         return SUCCESS;   
  
  10.      }   
  
  11.   
  
  12.     @Action("/user/action2")   
  
  13.     public String method2() {   
  
  14.         return SUCCESS;   
  
  15. }   
  
  16. }  
  

package com.example.web; 
  

import com.opensymphony.xwork2.Action; 
  
import com.opensymphony.xwork2.ActionSupport; 
  

public class HelloAction extends ActionSupport { 
  
    @Action("action1") 
  
    public String method1() { 
  
        return SUCCESS; 
  
    } 
  

    @Action("/user/action2") 
  
    public String method2() { 
  
        return SUCCESS; 
  
} 
  
} 
  

方法名 默认调用路径 默认映射路径 
  
method1 /hello!method1.action . /WEB-INF/content/hello.jsp 
  
method2 /hello!method2.action. /WEB-INF/content/hello.jsp 
  

通过@Action注释后 
  
方法名 @Action注释后调用路径 @Action注释 后映射路径 
  
method1 /action1!method1.action. /WEB-INF/content/action1.jsp 
  
method1 /user/action2!method2.action /WEB-INF/content/user/action2.jsp 
  


通过@Actions注释 
  
Java代码 复制代码 
  

   1. package com.example.web;   
  
   2.   
  
   3. import com.opensymphony.xwork2.ActionSupport;   
  
   4. import org.apache.struts2.convention.annotation.Action;   
  
   5. import org.apache.struts2.convention.annotation.Actions;   
  
   6.   
  
   7. public class HelloAction extends ActionSupport {   
  
   8.   @Actions({   
  
   9.     @Action("/different/url"),   
  
  10.     @Action("/another/url")   
  
  11.    })   
  
  12.   public String method1() {   
  
  13.     return “error”;   
  
  14.    }  
  

package com.example.web; 
  

import com.opensymphony.xwork2.ActionSupport; 
  
import org.apache.struts2.convention.annotation.Action; 
  
import org.apache.struts2.convention.annotation.Actions; 
  

public class HelloAction extends ActionSupport { 
  
  @Actions({ 
  
    @Action("/different/url"), 
  
    @Action("/another/url") 
  
  }) 
  
  public String method1() { 
  
    return “error”; 
  
  } 
  


我们可以通过:/different/url!method1.action 或 /another/url!method1.action 来调用method1 方法。 
  
对应的映射路径分别是/WEB-INF/content/different/url-error.jsp; /WEB-INF/content/another/url-error.jsp 
  

可能误导了大家,一个方法被@Action注释后,只是多了一种调用方式,而不是说覆盖了原来的调用方式。比如对于如下例子: 
  
Java代码 复制代码 
  

   1. package com.example.web;   
  
   2.   
  
   3. import com.opensymphony.xwork2.ActionSupport;   
  
   4. import org.apache.struts2.convention.annotation.Action;   
  
   5. import org.apache.struts2.convention.annotation.Actions;   
  
   6.   
  
   7. public class HelloAction extends ActionSupport {   
  
   8.   @Action("/another/url")   
  
   9.   public String method1() {   
  
  10.     return “error”;   
  
  11.    }  
  

package com.example.web; 
  

import com.opensymphony.xwork2.ActionSupport; 
  
import org.apache.struts2.convention.annotation.Action; 
  
import org.apache.struts2.convention.annotation.Actions; 
  

public class HelloAction extends ActionSupport { 
  
  @Action("/another/url") 
  
  public String method1() { 
  
    return “error”; 
  
  } 
  


我们调用method1方法可以通过两种方式: 
  
1 /hello!method1.action 映射 url:/WEB-INF/content/hello-error.jsp 
  
2 /another/url!method1.action 映射 url:/WEB-INF/content/another/url-error.jsp 
  
可见,两种方式均可对method1方法进行调用,唯一的区别就是,两种调用的映射是不一样的,所以,想跳转到不同的界面,这是一个非常好的选择。 
  


通过@Namespace 注释 
  
Java代码 复制代码 
  

   1. package com.example.web;   
  
   2.   
  
   3. import com.opensymphony.xwork2.ActionSupport;   
  
   4. import org.apache.struts2.convention.annotation.Action;   
  
   5. import org.apache.struts2.convention.annotation.Actions;   
  
   6. @Namespace("/other")   
  
   7. public class HelloWorld extends ActionSupport {   
  
   8.   
  
   9.   public String method1() {   
  
  10.     return “error”;   
  
  11.    }   
  
  12.     @Action("url")   
  
  13.   public String method2() {   
  
  14. return “error”;   
  
  15.    }   
  
  16.   
  
  17.     @Action("/different/url")   
  
  18.   public String method3() {   
  
  19. return “error”;   
  
  20.    }   
  
  21. }  
  

package com.example.web; 
  

import com.opensymphony.xwork2.ActionSupport; 
  
import org.apache.struts2.convention.annotation.Action; 
  
import org.apache.struts2.convention.annotation.Actions; 
  
@Namespace("/other") 
  
public class HelloWorld extends ActionSupport { 
  

  public String method1() { 
  
    return “error”; 
  
  } 
  
@Action("url") 
  
  public String method2() { 
  
return “error”; 
  
  } 
  

@Action("/different/url") 
  
  public String method3() { 
  
return “error”; 
  
  } 
  
} 
  



通过 /other/hello-world!method1.action 访问method1 方法。 
  
通过 /other/url!method2.action 访问method2 方法 
  
通过 /different /url!method3.action 访问method3 方法 
  
与@Action 注释不同的是,该注释覆盖了默认的namespace(这里是’/’),此时再用hello!method1.action 已经不能访问method1 了. 
  
@Results和@Result 
  
1 全局的(global)。 
  
全局results可以被action类中所有的action分享,这种results在action类上使用注解进行声明。 
  
Java代码 复制代码 
  

   1. package com.example.actions;   
  
   2.   
  
   3. import com.opensymphony.xwork2.ActionSupport;   
  
   4. import org.apache.struts2.convention.annotation.Action;   
  
   5. import org.apache.struts2.convention.annotation.Actions;   
  
   6. import org.apache.struts2.convention.annotation.Result;   
  
   7. import org.apache.struts2.convention.annotation.Results;   
  
   8.   
  
   9. @Results({   
  
  10.   @Result(name="failure", location="/WEB-INF/fail.jsp")   
  
  11. })   
  
  12. public class HelloWorld extends ActionSupport {   
  
  13.   public String method1() {   
  
  14.     return “failure”;   
  
  15.    }   
  
  16.     @Action("/different/url")   
  
  17.   public String method2() {   
  
  18.     return “failure”;   
  
  19.    }   
  
  20.   
  
  21. }  
  

package com.example.actions; 
  

import com.opensymphony.xwork2.ActionSupport; 
  
import org.apache.struts2.convention.annotation.Action; 
  
import org.apache.struts2.convention.annotation.Actions; 
  
import org.apache.struts2.convention.annotation.Result; 
  
import org.apache.struts2.convention.annotation.Results; 
  

@Results({ 
  
  @Result(name="failure", location="/WEB-INF/fail.jsp") 
  
}) 
  
public class HelloWorld extends ActionSupport { 
  
  public String method1() { 
  
    return “failure”; 
  
  } 
  
@Action("/different/url") 
  
  public String method2() { 
  
    return “failure”; 
  
  } 
  

} 
  


当我们访问 /hello -world !method1.action 时,返回 /WEB-INF/fail.jsp 
  
当我们访问 /hello -world !method2.action 时,返回 /WEB-INF/fail.jsp 
  
当我们访问 /different/url!method2.action 时,返回 /WEB-INF/fail.jsp



2 本地的(local)。
本地results只能在action方法上进行声明。
Java代码 复制代码

1. package com.example.actions;   
  
   2.   
  
   3. import com.opensymphony.xwork2.ActionSupport;   
  
   4. import org.apache.struts2.convention.annotation.Action;   
  
   5. import org.apache.struts2.convention.annotation.Actions;   
  
   6. import org.apache.struts2.convention.annotation.Result;   
  
   7. import org.apache.struts2.convention.annotation.Results;   
  
   8.   
  
   9. public class HelloWorld extends ActionSupport {   
  
  10.     @Action(value="/other/bar",results={@Result(name = "error", location = "www.baidu.com",type="redirect")})   
  
  11.   public String method1() {   
  
  12.     return “error”;   
  
  13.    }   
  
  14. }
package com.example.actions; 
  

import com.opensymphony.xwork2.ActionSupport; 
  
import org.apache.struts2.convention.annotation.Action; 
  
import org.apache.struts2.convention.annotation.Actions; 
  
import org.apache.struts2.convention.annotation.Result; 
  
import org.apache.struts2.convention.annotation.Results; 
  

public class HelloWorld extends ActionSupport { 
  
@Action(value="/other/bar",results={@Result(name = "error", location = "www.baidu.com",type="redirect")}) 
  
  public String method1() { 
  
    return “error”; 
  
  } 
  
}




当我们调用 /hello -world !method1.action 时,返回 /WEB-INF/content/hello-error.jsp
当我们调用 /other/bar!method1.action 时,返回 www.baidu.com