struts2-rest-plugin 的bug?如果是的话,这个插件的问题可真够多的!
用注释方式给rest controller 加上拦截器:
@InterceptorRefs({@InterceptorRef("authorization"),@InterceptorRef("defaultStack")})
发现加上拦截器后,立马蹦出来一堆怪问题!
该执行的不执行了:
/userinfos/3.xml
在执行show()之前,
本来该执行
setId(String id) 方法,但不执行。
本来不应该执行validate()方法,但竟然执行了!
不加拦截器的时候好好的!
晕死了。。。
如果真是struts2-rest-plugin 的bug,可真够过分的!
——本来怀疑是不是拦截器中 invocation.invoke()执行回调时,RestActionInvocation被替换成了DefaultActionInvocation,导致丢掉了rest的特性,但result仍然返回了xml,理应不是。可如果不是这个原因,又是什么原因呢?
==================================================
2010.2.24 这个问题解决了!
==================================================
今天上午忽然发现弄了半天是引用的拦截器栈的名字写错了!
但这个问题背后,其实是我对问题的本质没有理解。刚开始就想到会不会是 actionInvocation 由 restActionInvocation 被替换成了
DefaultActionInvocation所致。但却没往拦截器方面去想。
仔细看了一下 struts2-rest-plugin jar包里的 struts-plugin.xml,里面有个 "restDefaultStack",我继承人家的"rest-default"package,在引用人家的默认拦截器栈时,当然不应该引用传统的"defaultStack",而应该引用rest包的"restDefaultStack",这样id参数自然不会丢失了,validate方法的执行也不会有问题了——show/index都不会执行,update/create 都默认执行。
但是还有个问题。。。
==================================================
2010.2.24 新问题!
==================================================
我想给 controller 中的方法添加拦截器,并且希望拦截器能加在 method 级别上。
看了 convension plugin 的官方文档:
http://struts.apache.org/2.x/docs/convention-plugin.html#ConventionPlugin-Applying@Actionand@Actionsattheclasslevel
所幸文档写的很明确,是支持的!
但是按照文档的样子,照搬到 rest 方式的 controller 上,却有问题!
一段代码:
// PUT /orders/1
//@Action(interceptorRefs={@InterceptorRef("authorizationStack")})
@Action(interceptorRefs={@InterceptorRef("authorization"), @InterceptorRef("restDefaultStack")})
public String update() {
ordersService.save(model);
addActionMessage("Order updated successfully");
return "success";
}
// GET /orders/1
public HttpHeaders show() {
return new DefaultHttpHeaders("show");
}
// GET /orders
public HttpHeaders index() {
list = ordersService.getAll();
return new DefaultHttpHeaders("index")
.disableCaching();
}
本以为肯定只是拦截 update方法的,但当我访问show或index方法时,拦截器也一样都执行了!
百思不得其解!
所幸又从网上看到了这篇帖子:
http://topic.csdn.net/u/20091214/11/15a63547-92b6-448e-8ddf-143fe3d243f8.html
但该文中提到的是关于convention plugin 方式的,对于rest plugin来说,有其特殊的地方,就是无法给action添加自定义的名字——@Action(value="xxx");
rest方式下,如果要执行controller的update方法,会这样:
PUT /userinfos/{id}.xml
然后会执行对应controller中的update方法。也就是说,rest方式不会要求指定方法的名字。如果我硬给update指定一个:
@Action(value="update"),会报错!提示找不到对应的action。
难道rest方式下就没有办法将拦截器添加到method级别上么?郁闷。。。
<interceptors>
<interceptor name="exceptionInterceptor" />
<interceptor name="token" />
<interceptor name="validateInterceptor" />
<interceptor-stack name="cardDefaultStack">
<interceptor-ref name="servletConfig" />
<interceptor-ref name="token" />
<interceptor-ref name="prepare" />
<interceptor-ref name="actionMappingParams" />
<interceptor-ref name="modelDriven">
<param name="refreshModelBeforeResult">true</param>
</interceptor-ref>
<interceptor-ref name="fileUpload" />
<interceptor-ref name="staticParams" />
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*</param>
</interceptor-ref>
<interceptor-ref name="rest" />
<interceptor-ref name="conversionError" />
<interceptor-ref name="validateInterceptor" />
<interceptor-ref name="restWorkflow">
<param name="excludeMethods">input,back,cancel,browse,index,show,edit,editNew</param>
</interceptor-ref>
<interceptor-ref name="exceptionInterceptor" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="cardDefaultStack" />
<global-results>
<result name="error" type="dispatcher">/WEB-INF/jsp/error.jsp</result>
</global-results>
</package>
如上,有exceptionInterceptor,CardValidateInteceptor,TokenInteceptor是自定义拦截器。
2、挣对你的情况,开发个@interface最好,在拦截的方法上打个标签如:
@Validate //验证拦截器
public String create() {
log.debug("create order!");
return "success";
}
拦截器当拦截到请求会先根据标签判断是不是需要处理的。
3、自定义拦截器
public class TokenInteceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
String method = invocation.getProxy().getMethod();
Object action = invocation.getAction();
// 获取当前调用的方法
Method invokeMethod = action.getClass().getMethod(method, new Class[] {});
if ("POST".equals(request.getMethod()) && StringUtils.isNotBlank(method)
&& invokeMethod.isAnnotationPresent(CardToken.class)) {
// 获取请求中的TOKEN
String token = request.getParameter(GenerateToken.COMMIT_TOKEN);
// 如果有TOKEN,说明需要进行表单重复提交验证
if (!StringUtils.isBlank(token)) {
PrintWriter writer = null;
try {
if (!validToken(token)) {
HttpServletResponse resp = ServletActionContext.getResponse();
writer = resp.getWriter();
writer.write(TIPS_MSG);
return null;
}
} catch (Exception e) {
throw new CardRunException(e);
} finally {
IOUtils.closeQuietly(writer);
}
} else {
throw new CardRunException("token使用非法,post提交数据中,没有发现参数" + GenerateToken.COMMIT_TOKEN + ",或参数"
+ GenerateToken.COMMIT_TOKEN + "为空");
}
}
return invocation.invoke();
}
}
如上invokeMethod.isAnnotationPresent(CardToken.class))就是处理标记的方法,希望对你有帮助。
2 楼 mht19840918 2010-02-24 忘了说了,你还是没有搞清楚Struts2拦截器的原理,还是要好好看看。Struts2 rest本身就是借助拦截器实现的,所以你这个问题与rest一点关系都没有
最后补充下上面CardToken标记代码:
@Retention(RetentionPolicy.RUNTIME)
@Target( {
ElementType.METHOD
})
public @interface CardToken {
}
3 楼 rustlingwind 2010-02-24 mht19840918 写道忘了说了,你还是没有搞清楚Struts2拦截器的原理,还是要好好看看。Struts2 rest本身就是借助拦截器实现的,所以你这个问题与rest一点关系都没有
最后补充下上面CardToken标记代码:
@Retention(RetentionPolicy.RUNTIME)
@Target( {
ElementType.METHOD
})
public @interface CardToken {
}
呵呵,非常感谢!我还跟琨哥说让他代我谢谢你!
真有一种雪中送碳的感觉,这个问题已经困扰我一天了!
看过你的回复,我已经明白了你的意思。用自定义注释的方式,借助struts2的拦截器思想,来完成对 action的过滤。
我马上试一下!