`
rustlingwind
  • 浏览: 20910 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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级别上么?郁闷。。。




1
0
分享到:
评论
3 楼 rustlingwind 2010-02-24  
mht19840918 写道
忘了说了,你还是没有搞清楚Struts2拦截器的原理,还是要好好看看。Struts2 rest本身就是借助拦截器实现的,所以你这个问题与rest一点关系都没有
最后补充下上面CardToken标记代码:

@Retention(RetentionPolicy.RUNTIME)
@Target( {
    ElementType.METHOD
})
public @interface CardToken {
}

呵呵,非常感谢!我还跟琨哥说让他代我谢谢你!
真有一种雪中送碳的感觉,这个问题已经困扰我一天了!

看过你的回复,我已经明白了你的意思。用自定义注释的方式,借助struts2的拦截器思想,来完成对 action的过滤。

我马上试一下!
2 楼 mht19840918 2010-02-24  
忘了说了,你还是没有搞清楚Struts2拦截器的原理,还是要好好看看。Struts2 rest本身就是借助拦截器实现的,所以你这个问题与rest一点关系都没有
最后补充下上面CardToken标记代码:

@Retention(RetentionPolicy.RUNTIME)
@Target( {
    ElementType.METHOD
})
public @interface CardToken {
}
1 楼 mht19840918 2010-02-24  
受朋友之托,简单说下:
1、我讨厌用注释方式配拦截器,所以少了直接陪在struts.xml中代码如下:
<package name="card" extends="rest-default">
    <interceptors>
      <interceptor name="exceptionInterceptor" class="com.fost.card.webcommon.ExceptionInterceptor" />
      <interceptor name="token" class="com.fost.card.webcommon.token.TokenInteceptor" />
      <interceptor name="validateInterceptor" class="com.fost.card.webcommon.validate.CardValidateInteceptor" />
      <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))就是处理标记的方法,希望对你有帮助。

相关推荐

Global site tag (gtag.js) - Google Analytics