`

页面跳转到BackURL功能(基于struts2实现)

阅读更多
页面跳转到BackURL功能(基于struts2实现)
应用场景:
在查询用户列表/user!list.action时,点击新增用户,将跳转到/user!input.action
现在需要新增成功后跳转到/user!list.action
常规的做法是在UserAction里定义一个Result=RELOAD, location = "user!list.action", type = "redirect"

但是现在有这样的需求:
在查询用户列表/user!list.action,作了一些查询,分页的操作后,可能你的页面地址就变成了:
/user!list.action?page.no=5&page.size=20&searchParam=searchValue
如果此时你点击新增用户,需要新增完毕仍回到刚才操作过的页面,因为这个页面地址已经无法预料,你无法再Action里配置

所以现在需要实现这样的功能,能方便跳回之前操作的页面

目前实现的大致思路如下:

1.首先当跳转到新增页面时,一定要把跳回的URL地址带上,如:
/user!input.action?BackURL=/user!list.action?page.no=5&page.size=20&searchParam=searchValue
带上的地址为了不影响浏览器解析原地址,已改将BackURL的值URL Encode如下
/user!input.action?BackURL=%2Fuser%21list.action%3Fpage.no%3D5%26page.size%3D20%26searchParam%3DsearchValue

2.当新增页面提交时,要将BackURL一并提交给服务器,所以最简单的做法是在Form表单加上一个隐藏域:
<input type="hidden" name="BackURL" value="${param['BackURL']}">

或者提交表单时动态修改提交的URL为/user!save.action?BackURL=%2Fuser%21list.action%3Fpage.no%3D5%26page.size%3D20%26searchParam%3DsearchValue

3.新增用户页面/user!input.action会将请求提交给UserAciton的save方法处理
  1. public String save() throws Exception {  
  2.     //...  
  3.     userService.save(user);  
  4.     //...  
  5.     return RELOAD;  
  6. }  

4.为了做到通用,一般web项目都会自定义一个类似AuthorityInterceptor来鉴权的拦截器

  1. public class AuthorityInterceptor extends AbstractInterceptor {  
  2.   
  3.   
  4.     public String intercept(ActionInvocation invocation) throws Exception {  
  5.         //...  
  6.         //此处注册一个Action结果前处理监听器,action的方法执行完,在返回结果视图之前会调用此监听器  
  7.         //我们的核心逻辑都在这个监听器BackURLPreResultListener完成。  
  8.         invocation.addPreResultListener(new BackURLPreResultListener());  
  9.         String invokeResult = invocation.invoke();  
  10.         return invokeResult;  
  11.     }  
  12. }  

  1. public class BackURLPreResultListener implements PreResultListener {  
  2.   
  3.   
  4.     @Override  
  5.     public void beforeResult(ActionInvocation invocation, String resultCode) {  
  6.         // handle backUrl  
  7.         HttpServletRequest request = ServletActionContext.getRequest();  
  8.         String backURL = extractBackURL(request);  
  9.         ServletActionContext.getResponse().sendRedirect(backURL);  
  10.         invocation.setResultCode(Action.NONE);  
  11.     }  
  12. }  

从request里提取backUrl的方法extractBackURL每个人实现都不太一样,我的实现是这样的:

  1. /** 
  2.  * 上一次请求的地址  
  3.  * 1、先从request.parameter中查找BackURL  
  4.  * 2、先从request.attribute中查找BackURL 
  5.  * 3、先从request.Referer header中查找BackURL,即当前请求是从哪个页面触发的 
  6.  */  
  7. private String extractBackURL(HttpServletRequest request) {  
  8.     String url = request.getParameter(Const.BACK_URL);  
  9.     if (url == null) {  
  10.         url = (String) request.getAttribute(Const.BACK_URL);  
  11.     }  
  12.     if (url == null) {  
  13.         url = (String) request.getHeader("Referer");  
  14.     }  
  15.     if (!StringUtils.isEmpty(url) && url.startsWith(request.getContextPath())) {  
  16.         url = getBasePath(request) + url;  
  17.     }  
  18.     return url;  
  19. }  
  20.   
  21.   
  22. private String getBasePath(HttpServletRequest req) {  
  23.     StringBuffer baseUrl = new StringBuffer();  
  24.     String scheme = req.getScheme();  
  25.     int port = req.getServerPort();  
  26.     baseUrl.append(scheme); // http, https  
  27.     baseUrl.append("://");  
  28.     baseUrl.append(req.getServerName());  
  29.     if ((scheme.equals("http") && port != 80) || (scheme.equals("https") && port != 443)) {  
  30.         baseUrl.append(':');  
  31.         baseUrl.append(req.getServerPort());  
  32.     }  
  33.     return baseUrl.toString();  
  34. }  

到此跳转的基本功能都已实现,但是有几个问题,还需要更完善一下:

1.不是每个页面传了BackURL参数,你就需要给它跳回BackURL指定的页面,如
/user!input.action?BackURL=/user!list.action?page.no=5&page.size=20&searchParam=searchValue
当进入新增用户界面时,也会经过BackURLPreResultListener监听器处理。
而真正要处理的只是save方法执行后要跳转

所以我们还得为这段代码加一个开关,即什么时候执行,什么时候不执行,改造过后的代码如下:

  1. public class BackURLPreResultListener implements PreResultListener {  
  2.   
  3.   
  4.     @Override  
  5.     public void beforeResult(ActionInvocation invocation, String resultCode) {  
  6.         // handle backUrl  
  7.         HttpServletRequest request = ServletActionContext.getRequest();  
  8.         if (Boolean.TRUE.equals(request.getAttribute(Const.ENABLE_BACK_URL))) {  
  9.             String backURL = extractBackURL(request);  
  10.             if (StringUtils.isNotBlank(backURL)) {  
  11.                 ServletActionContext.getResponse().sendRedirect(backURL);  
  12.                 invocation.setResultCode(Action.NONE);  
  13.             }  
  14.         }  
  15.     }  
  16. }  

即检查request里是否有Attribute,key为Const.ENABLE_BACK_URL,value为True,如果有,则视为当前action方法执行完后需要跳转

而在save方法里我们应该往request里给这个开关变量赋值True
  1. public String save() throws Exception {  
  2.     //...  
  3.     userService.save(user);  
  4.     //...  
  5.     ServletActionContext.getRequest().setAttribute(Const.ENABLE_BACK_URL, Boolean.TRUE);  
  6.     return RELOAD;  
  7. }  

save方法里返回RELOAD改不改都无所谓,因为后面BackURLPreResultListener会把这个执行结果改成NONE的

在此基本上整个功能都已实现完毕,但是还有一个小小的缺憾:
一般在save方法里,我们都会加如下代码:来让页面可以展示保存成功的消息:
addActionMessage("保存用户成功!");

因为Struts2里的MessageStoreInterceptor会将这些消息存在session里,等Reload的方法list执行完毕后才会清空,这样在list页面我们依然能取到save方法里放进去的提示消息
而MessageStoreInterceptor判断是否需要将消息存在session里的逻辑是
save方法返回的结果Result是否是redirect类型的,具体可以参考源代码逻辑:

  1. protected void after(ActionInvocation invocation, String result) throws Exception {  
  2.   
  3.   
  4.     String reqOperationMode = getRequestOperationMode(invocation);  
  5.         //result的类型是redirect时才会把actionMessage放入session里  
  6.     boolean isRedirect = invocation.getResult() instanceof ServletRedirectResult;  
  7.     if (STORE_MODE.equalsIgnoreCase(reqOperationMode) ||  
  8.         STORE_MODE.equalsIgnoreCase(operationMode) ||  
  9.         (AUTOMATIC_MODE.equalsIgnoreCase(operationMode) && isRedirect)) {  
  10.   
  11.   
  12.         Object action = invocation.getAction();  
  13.         if (action instanceof ValidationAware) {  
  14.         // store error / messages into session  
  15.         Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION);  
  16.   
  17.   
  18.         LOG.debug("store action ["+action+"] error/messages into session ");  
  19.   
  20.   
  21.         ValidationAware validationAwareAction = (ValidationAware) action;  
  22.         session.put(actionErrorsSessionKey, validationAwareAction.getActionErrors());  
  23.         session.put(actionMessagesSessionKey, validationAwareAction.getActionMessages());  
  24.         session.put(fieldErrorsSessionKey, validationAwareAction.getFieldErrors());  
  25.         }  
  26.         else {  
  27.         LOG.debug("Action ["+action+"] is not ValidationAware, no message / error that are storeable");  
  28.         }  
  29.     }  
  30. }  

所以我们知道struts2框架的部分运行逻辑后,可以很容易得出我们的解决方法:
即在struts.xml配置文件中,新增一个全局的result:

  1. <global-results>  
  2.     ...  
  3.     <!-- 在此处定义redirect的backUrl主要是为了利用struts2拦截器MessageStoreInterceptor的便利,即重定向后,actionMessage里的数据都不会丢失 -->  
  4.     <result name="backUrl" type="redirect">${#request['backUrl']}</result>  
  5. </global-results>  

而此处${#request['backUrl']}的 表达式写法可以从request里拿出真正需要跳转的backUrl的地址

我们只要将BackURLPreResultListener修改如下,就可以让我们的跳转经过MessageStoreInterceptor处理

  1. public class BackURLPreResultListener implements PreResultListener {  
  2.   
  3.   
  4.     @Override  
  5.     public void beforeResult(ActionInvocation invocation, String resultCode) {  
  6.         // handle backUrl  
  7.         HttpServletRequest request = ServletActionContext.getRequest();  
  8.         if (Boolean.TRUE.equals(request.getAttribute(Const.ENABLE_BACK_URL))) {  
  9.             String backURL = extractBackURL(request);  
  10.             if (StringUtils.isNotBlank(backURL)) {  
  11.                 //此处不使用response.sendRedirect来重定向,主要是为了利用struts2拦截器MessageStoreInterceptor的便利,即重定向后,actionMessage里的数据都不会丢失的功能  
  12.                 //ServletActionContext.getResponse().sendRedirect(backURL);  
  13.                 ServletActionContext.getRequest().setAttribute("backUrl", backURL);  
  14.                 invocation.setResultCode("backUrl");  
  15.             }  
  16.         }  
  17.     }  
分享到:
评论

相关推荐

    sp\button和submit的区别及使用js实现页面跳转的方式

    sp\button和submit的区别及使用js实现页面跳转的方式 Button和Submit是两个常用的HTML表单元素,它们都是用于提交表单的,但是它们之间存在着一些区别。 首先,Button类型的按钮仅仅是一个普通的按钮,点击它不会...

    Javascript页面跳转常见实现方式汇总

    本文实例总结了Javascript页面跳转常见实现方式。分享给大家供大家参考,具体如下: 概述 相信很多Web开发者都知道,在开发Web程序的时候,对于页面之间的跳转,有很多种,但是有效的跳转则事半功倍,下面就是我在...

    JavaScript实现页面跳转的几种常用方式

    本文实例讲述了JavaScript实现页面跳转的几种常用方式。分享给大家供大家参考,具体如下: 第一种: [removed] [removed].href="login.jsp?backurl="+[removed].href; [removed] 第二种: [removed] alert&#...

    js页面跳转常用的几种方式

    backurl=”+[removed].href; [removed] 第二种: 代码如下: [removed] alert&#40;“返回”&#41;; window.history.back(-1); [removed] 第三种: 代码如下: [removed] window.navigate(“jb51.jsp

    js实现页面跳转的五种方法推荐

    js实现页面跳转的五种方法推荐 第一种: 复制代码 代码如下: [removed] [removed].href=”xx.jsp?backurl=”+[removed].href; [removed] 第二种: 复制代码 代码如下: [removed] alert&#40;“返回”&#41;; ...

    用js实现页面返回的5个方法

    用js实现页面返回的5个方法 第一种: &lt;script language="javascript" type="text/javascript"&gt; window.location.href="login.jsp?backurl="+window.loca

    Javascript实现页面跳转的几种方式分享

    backurl=”+[removed].href; [removed]第二种: 代码如下:[removed]alert&#40;“返回”&#41;;window.history.back(-1);[removed]第三种: 代码如下:[removed]window.navigate(“top.jsp”);[removed]第四种

    js 控制页面跳转的5种方法

    第一种: 代码如下: [removed] ...backurl=”+[removed].href; [removed]第二种: 代码如下: [removed]alert&#40;“返回”&#41;;window.history.back(-1); [removed]第三种: 代码如下: [removed]window.nav

    SinaTair(微博air客户端)免费下载 v1.67.zip

    SinaTair新浪微博桌面客户端(微博air客户端)基于air平台的微博桌面客户端,界面简洁,功能实用,支持动态表情显示,支持gif动画显示,还有涂鸦板,让你“画”说微博,强大的配置功能,根据您的个性随需定制,追求...

    identification.exe

    基于opencv的自开发程序,使用pyinstaller打包生成exe,可在windows直接使用,命令示例youfile.exe 0.75 2 ./1.jpg ./2.jpg

    E7机原理及接口技术课后习题答案三[1]

    E7机原理及接口技术课后习题答案三[1]

    sina,网易,搜狐,腾讯等微博OAuth认证代码

    整合了sina、网易、搜狐和腾讯微博的Oauth认证代码,非常实用的demo。

    Sina News IOS

    自己写的一个 采用Sina RSS作为数据源的IOS 移动端程序。里面包括了JSON解析,网络通讯,Tab分页,侧边栏等多个常用功能,练手和IOS学习的好资料

    yxpjqrj.rar

    Accept: */* ...UA-CPU: x86 Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1) Host: entry.yeah.net ...url=&username=fsdf&type=&password=vng&domain=yeah.net&.done=...

    javascript 跳转代码集合

    backurl=”+[removed].href; [removed] 第二种: [removed] alert&#40;”返回”&#41;; window.history.back(-1); [removed] 第三种: [removed] window.navigate(”top.jsp”); [removed] 第四种: &lt;scr

    javascript 网页跳转的方法

    backurl=\”+[removed].href; [removed] 第二种: [removed] alert&#40;“返回”&#41;; window.history.back(-1); [removed] 第三种: [removed] window.navigate(“top.jsp”); [removed] 第四种: &lt;sc

    单点登录源码

    微信公众号管理平台,除实现官网后台自动回复、菜单管理、素材管理、用户管理、消息群发等基础功能外,还有二维码推广、营销活动、微网站、会员卡、优惠券等。 &gt; zheng-wechat-app 微信小程序后台 ## 环境搭建...

    基于docent的基础:基于docent MRA的基础应用

    基地 用离子构建的前端应用程序。 依存关系 Android工作室 离子性 ... 导出const BackUrl =“ ” 配置config.xml 修改config.xml:小部件id =“ com.ionic.docent” 端到端测试 运行:npm运行柏树

    sina微博auoth代码

    sina微博auoth未例代码,用于连接sina微博应用连接的

    helpline-frontend

    helpline-frontend是一项将按需显示联系页面的服务。 跑步 要运行该应用程序,请使用sbt run 。 原料药 方法 小路 描述 得到 / helpline /:helpKey?back = backURL 按需显示联系页面 例如/ helpline / deadsed?...

Global site tag (gtag.js) - Google Analytics