论坛首页 Java企业应用论坛

在Tapestry中使用redirect-after-post模式控制表单提交

浏览 7639 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2006-11-27  
Tapestry中表单的提交有很多问题,刷新导致表单的重复提交、臭名昭著的dirty form warning
显示不友好的URL等,这些都可以使用redirect-after-post模式来避免,也就是post表单之后将页面redirect一下,
这样地址栏显示的就是
redirect之后的页面,刷新的也将是这个页面,而且redirect之后的页面一般为Page页面,
可以使用
friendlyUrl来显示友好的url<o:p></o:p>
要使用这个模式,不得不提到一个ILink接口,T4中可接受的listener方法中一个方法签名是这样的:<o:p></o:p>
java 代码
 
  1. public ILink listenerMethod(Parameter parameter); 

这个方法的返回值为ILink,如果T4侦测到是这个返回值的话,将自动redirectILink指定的页面。
而且
ILink可以为纯页面的Link也可以是带有参数的Link。纯页面Link也就是page service生成的link
带参数的
linkexternal service生成的link,具体link的生成代码如下:<o:p></o:p>  

java 代码
 
  1. @InjectObject("engine-service:page")  
  2.   
  3.   public abstract IEngineService getPageService();  
  4.   
  5.        public ILink getPageLink(String pageName) {  
  6.   
  7.            return getPageService().getLink(false, pageName);  
  8.   
  9.   }  
  10.   
  11.   
  12.   
  13.       @InjectObject("engine-service:external")  
  14.   
  15.   public abstract IEngineService getExternalService();  
  16.   
  17.        public ILink getExternalLink(String pageName, Object[] parameters) {  
  18.   
  19.      ExternalServiceParameter esp = new ExternalServiceParameter(pageName,  
  20.   
  21.             parameters);  
  22.   
  23.      return getExternalService().getLink(false, esp);  
  24.   
  25.   }  
<o:p></o:p>

将上述方法放入到你的项目中自定义的MyBasePage(继承TapestryBasePage)中,然后让你的

页面类继承MyBasePage,这样就可以使用上述方法了。<o:p></o:p>

这样在页面提交时可以如下调用
java 代码
 
  1. public ILink onDelete(Long id){  
  2.   
  3.    
  4.   
  5. //your code to delete object  
  6.   
  7.    
  8.   
  9. return getPageLink(getPageName());//删除后重新redirect到本页面  
  10.   
  11. }  
  12.   
  13. 当然如果这个页面带参数,例如catgoryId=1,则代码如下:  
  14.   
  15. public ILink onDelete(Long id){  
  16.   
  17.    
  18.   
  19. //your code to delete object  
  20.   
  21.    
  22.   
  23. return getExternalLink(getPageName(),new Object[]{categoryId});  
  24. //删除后重新redirect到本页面,并带一个参数  
  25.   
  26. }  
上述方式通过继承来实现方法调用,感觉不是很好,我们可以利用Tapestry的易扩展特性来扩展它,
首先想到的就是注释方式来注入,例如我想实现如下注入
java 代码
 
  1. @InjectPageLink(“Home“)  
  2.   
  3. Public abstract ILink getHomeLink();  
  4.   
  5.    
  6.   
  7. @InjectExternalLink(“Category“)  
  8.   
  9. Public abstract ILink getCategoryLink(Long categoryId);  

这样即去掉了页面的继承,也显得比较直接。
首先是定义注释类
java 代码
 
  1. import java.lang.annotation.ElementType;  
  2.   
  3. import java.lang.annotation.Retention;  
  4.   
  5. import java.lang.annotation.RetentionPolicy;  
  6.   
  7. import java.lang.annotation.Target;  
  8.   
  9.    
  10.   
  11. @Target( { ElementType.METHOD })  
  12.   
  13. @Retention(RetentionPolicy.RUNTIME)  
  14.   
  15. public @interface InjectPageLink {  
  16.   
  17.     String value();  
  18.   
  19. }  

<o:p></o:p>

然后写注释的worker
<o:p></o:p>
java 代码
 
  1. import java.lang.reflect.Method;  
  2.   
  3. import java.lang.reflect.Modifier;  
  4.   
  5.    
  6.   
  7. import org.apache.hivemind.ApplicationRuntimeException;  
  8.   
  9. import org.apache.hivemind.Location;  
  10.   
  11. import org.apache.hivemind.service.BodyBuilder;  
  12.   
  13. import org.apache.hivemind.service.MethodSignature;  
  14.   
  15. import org.apache.tapestry.annotations.AnnotationUtils;  
  16.   
  17. import org.apache.tapestry.annotations.MethodAnnotationEnhancementWorker;  
  18.   
  19. import org.apache.tapestry.engine.ILink;  
  20.   
  21. import org.apache.tapestry.enhance.EnhancementOperation;  
  22.   
  23. import org.apache.tapestry.spec.IComponentSpecification;  
  24.   
  25.    
  26.   
  27. public class InjectPageLinkAnnotationWorker implements  
  28.   
  29.        MethodAnnotationEnhancementWorker {  
  30.   
  31.    
  32.   
  33.     public void performEnhancement(EnhancementOperation op,  
  34.   
  35.            IComponentSpecification spec, Method method, Location location) {  
  36.   
  37.        if (!method.getReturnType().equals(ILink.class))  
  38.   
  39.            throw new ApplicationRuntimeException(  
  40.   
  41.                   "InjectPage annotation must return ILink");  
  42.   
  43.    
  44.   
  45.        InjectPageLink injectPageLink = method  
  46.   
  47.               .getAnnotation(InjectPageLink.class);  
  48.   
  49.    
  50.   
  51.        String pageName = injectPageLink.value();  
  52.   
  53.    
  54.   
  55.        BodyBuilder builder = new BodyBuilder();  
  56.   
  57.    
  58.   
  59.        builder.begin();  
  60.   
  61.        builder  
  62.   
  63.               .addln(  
  64.   
  65.                      "return getPage().getRequestCycle().getInfrastructure().getServiceMap().getService( 
  66.  
  67. org.apache.tapestry.Tapestry.PAGE_SERVICE).getLink(false,\"{0}\");",  
  68.   
  69.                      pageName);  
  70.   
  71.    
  72.   
  73.        builder.end();  
  74.   
  75.    
  76.   
  77.        op.addMethod(Modifier.PUBLIC, new MethodSignature(method), builder  
  78.   
  79.               .toString(), location);  
  80.   
  81.    
  82.   
  83.        if (isGetter(method))  
  84.   
  85.            op.claimReadonlyProperty(AnnotationUtils.getPropertyName(method));  
  86.   
  87.     }  
  88.   
  89.    
  90.   
  91.     boolean isGetter(Method method) {  
  92.   
  93.        return method.getName().startsWith("get")  
  94.   
  95.               && method.getParameterTypes().length == 0;  
  96.   
  97.     }  
  98.   
  99. }  

External
的同样两个类<o:p></o:p>
java 代码
 
  1. import java.lang.annotation.ElementType;  
  2.   
  3. import java.lang.annotation.Retention;  
  4.   
  5. import java.lang.annotation.RetentionPolicy;  
  6.   
  7. import java.lang.annotation.Target;  
  8.   
  9.    
  10.   
  11. @Target( { ElementType.METHOD })  
  12.   
  13. @Retention(RetentionPolicy.RUNTIME)  
  14.   
  15. public @interface InjectExternalLink {  
  16.   
  17.     String value();  
  18.   
  19. }  

<o:p></o:p>

Work
java 代码
 
  1. import java.lang.reflect.Method;  
  2.   
  3. import java.lang.reflect.Modifier;  
  4.   
  5.    
  6.   
  7. import org.apache.hivemind.ApplicationRuntimeException;  
  8.   
  9. import org.apache.hivemind.Location;  
  10.   
  11. import org.apache.hivemind.service.BodyBuilder;  
  12.   
  13. import org.apache.hivemind.service.MethodSignature;  
  14.   
  15. import org.apache.tapestry.Tapestry;  
  16.   
  17. import org.apache.tapestry.annotations.AnnotationUtils;  
  18.   
  19. import org.apache.tapestry.annotations.MethodAnnotationEnhancementWorker;  
  20.   
  21. import org.apache.tapestry.engine.ILink;  
  22.   
  23. import org.apache.tapestry.enhance.EnhancementOperation;  
  24.   
  25. import org.apache.tapestry.spec.IComponentSpecification;  
  26.   
  27.    
  28.   
  29. public class InjectExternalLinkAnnotationWorker implements  
  30.   
  31.        MethodAnnotationEnhancementWorker {  
  32.   
  33.    
  34.   
  35.     public void performEnhancement(EnhancementOperation op,  
  36.   
  37.            IComponentSpecification spec, Method method, Location location) {  
  38.   
  39.        if (!method.getReturnType().equals(ILink.class))  
  40.   
  41.            throw new ApplicationRuntimeException(  
  42.   
  43.                   "injectExternalLink annotation must return ILink");  
  44.   
  45.    
  46.   
  47.        InjectExternalLink injectExternalLink = method  
  48.   
  49.               .getAnnotation(InjectExternalLink.class);  
  50.   
  51.    
  52.   
  53.        String pageName = injectExternalLink.value();  
  54.   
  55.    
  56.   
  57.        BodyBuilder builder = new BodyBuilder();  
  58.   
  59.    
  60.   
  61.        builder.begin();  
  62.   
  63.    
  64.   
  65.        Class[] parameterTypes = method.getParameterTypes();  
  66.   
  67.        int paramCount = Tapestry.size(parameterTypes);  
  68.   
  69.    
  70.   
  71.        if (paramCount > 0) {  
  72.   
  73.            if (parameterTypes[0].isArray()) {  
  74.   
  75.               builder  
  76.   
  77.                      .addln(  
  78.   
  79.                             "return getPage().getRequestCycle().getInfrastructure().getServiceMap(). 
  80.  
  81. getService(org.apache.tapestry.Tapestry.EXTERNAL_SERVICE).getLink(false, new org.apache.tapestry.engine.ExternalServiceParameter(\"{0}\",$1));",  
  82.   
  83.                             pageName);  
  84.   
  85. else {  
  86.   
  87.               builder  
  88.   
  89.                      .addln(  
  90.   
  91.                             "java.lang.Object[] params = new java.lang.Object[{0}];",  
  92.   
  93.                             paramCount);  
  94.   
  95.               for (int i = 0; i < paramCount; i++) {  
  96.   
  97.                   builder.add("params[{0}] = ", i);  
  98.   
  99.    
  100.   
  101.                   if (parameterTypes[i].isPrimitive())  
  102.   
  103.                      builder.add("($w) ");  
  104.   
  105.    
  106.   
  107.                   // Parameter $0 is this, so $1 is the first real parameter.  
  108.   
  109.    
  110.   
  111.                   builder.addln("${0};", i + 1);  
  112.   
  113.               }  
  114.   
  115.               builder  
  116.   
  117.                      .addln(  
  118.   
  119.                             "return getPage().getRequestCycle().getInfrastructure().getServiceMap().getService(org.apache.tapestry.Tapestry.EXTERNAL_SERVICE).getLink(false, new org.apache.tapestry.engine.ExternalServiceParameter(\"{0}\",params));",  
  120.   
  121.                             pageName);  
  122.   
  123.            }  
  124.   
  125.        }  
  126.   
  127.    
  128.   
  129.        builder.end();  
  130.   
  131.    
  132.   
  133.        op.addMethod(Modifier.PUBLIC, new MethodSignature(method), builder  
  134.   
  135.               .toString(), location);  
  136.   
  137.    
  138.   
  139.        if (isGetter(method))  
  140.   
  141.            op.claimReadonlyProperty(AnnotationUtils.getPropertyName(method));  
  142.   
  143.     }  
  144.   
  145.    
  146.   
  147.     boolean isGetter(Method method) {  
  148.   
  149.        // We already know the return type is String  
  150.   
  151.    
  152.   
  153.        return method.getName().startsWith("get")  
  154.   
  155.               && method.getParameterTypes().length == 0;  
  156.   
  157.     }  
  158.   
  159. }  
<o:p></o:p>
类写好了,然后就是贡献到Tapestry<o:p></o:p>
xml 代码
 
  1. <module id="your.model.annotation" version="1.0.0" package="your.package.annotations">  
  2.   
  3.      <contribution configuration-id="tapestry.annotation.MethodWorkers">  
  4.   
  5.       <worker annotation="InjectPageLink" object="instance:InjectPageLinkAnnotationWorker"/>  
  6.   
  7.       <worker annotation="InjectExternalLink" object="instance:InjectExternalLinkAnnotationWorker"/>  
  8.   
  9.     <!---->contribution>  
  10.   
  11. <!---->module>  

<o:p></o:p>
好了将你的module放入项目hivemodule.Xml作为子module<o:p></o:p>
Okenjoy it

<o:p></o:p>

<o:p> </o:p>

   发表时间:2006-11-29  
不错,又学到了新东西。

不过,就应用上讲,我的处理方式跟楼主提到的不一样。

一是listener方法返回void,自己用cycle来激活。看起来直接。
如果仅仅是跳转,直接激活。如果需要refresh,直接通过ILink抛出异常算了。

二是不太看好这种refresh的实际价值。
我的使用经验是,listener方法是对application中的page和component状态进行控制、设置和初始化的重要环节,如果 refresh掉,那么状态就丢失了,重新找状态很费事。这个listener就失去相当的价值。如果页面状态很简单,这倒是ok。但是如果状态复杂,就几乎没有这种可能性了。

根本原因,是因为url是service调用接口的表现,而不是service如何执行的表现。这可能是没有办法的事情。记得胡子老大自己也说过,跟其他框架比起来,要追求漂亮的url,在tapestry里面奋战没有胜算。





0 请登录后投票
   发表时间:2006-11-29  
是呀,tapestry的经典处理流程就是get page->set state->active
active调用的是forward方法,就是当前页面的实际页面与url上指示的页面不相符,这也无可厚非。tapestry如果真的要完全的OO的话,屏蔽URL上的处理是必须的。
取得ILink来redirect的一个好处是可以防止form提交后refresh。
0 请登录后投票
   发表时间:2006-11-29  
说到根本上,是概念不一致造成的问题。

从web来看,url本身是一种uri,用于资源定位。但是从web开发实践看来,更多的把他作为web api的包装器。这种冲突,在ajax应用方面也有体现。

url做为uri,被设计为没有状态的,但是做为api,就会有状态。如果不能让那些设计标准的专业人士认识到这种差距,那么这种尴尬总会存在。
0 请登录后投票
   发表时间:2006-12-04  
太好了,mark,我前一段时间正在想解决这样的问题呢
不过现在先不能看,等考完SCJP和学期末考试再说
0 请登录后投票
   发表时间:2006-12-18  
InjectPageLink和InjectExternalLink已经提交到tapestry的jira中,将集成到4.1.2版本中,呵呵,也算对开源做了贡献。

详细信息:
http://issues.apache.org/jira/browse/TAPESTRY-1184?page=history
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics