从URL中检索片段(哈希)并将值注入到Bean中。

16
我正在寻找一种方法,可以像注入查询参数值一样,将URL片段(#)中的值注入到bean(JSF)中。我正在使用Ben Alman的可书签化jQuery插件(http://benalman.com/projects/jquery-bbq-plugin/)来创建URL片段。我希望使用PrettyFaces的自定义正则表达式模式来解决我的问题,但迄今为止我还没有成功。
http://ocpsoft.com/docs/prettyfaces/snapshot/en-US/html_single/#config.pathparams.regext
我想在这里定义我的情况,如果有人有想法,我愿意尝试。
我正在使用RichFaces:3.3.3、Spring:3.0.2.RELEASE、Hibernate:3.5.3-Final、JSF:2.0.2-FCS和PrettyFaces:3.0.1。
Web应用程序生成以下类型的URL,其中参数在哈希(#)之后列出。想法是拥有基于ajax的可书签化URL。因此,每次单击更改系统状态的元素时,该值都会通过ajax发送到服务器,并且哈希后面的URL将被重写。哈希后面可以有1到3个参数,参数数量是可选的。
我的目标是,当用户收藏带有哈希的URL并重新访问保存的页面时,页面应将正确的值注入到系统中,并在以前的状态下可视化该页面(类似于查询参数)。
在下面,我有一个正则表达式,可以捕获哈希后面的所有参数。
//URL:   
http://localhost:8080/nymphaea/workspace/#node=b48dd073-145c-4eb6-9ae0-e1d8ba90303c&lod=75e63fcd-f94a-49f5-b0a7-69f34d4e63d7&ln=en

//Regular Expression:    
\#(\w*\=(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}))|\&(\w*\=(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}))|\&(\w*\=\w{2})

我知道有一些网站可以将URL片段发送到服务器端逻辑中,

是否有任何方法可以将URL片段的值注入到服务器端的bean中?

5个回答

8
你可以使用window.onhashchange来实现,它会填充一个隐藏表单的输入字段,并在输入字段发生变化时异步提交表单。以下是Facelets页面的示例:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>SO question 3475076</title>
        <script>
            window.onload = window.onhashchange = function() {
                var fragment = document.getElementById("processFragment:fragment");
                fragment.value = window.location.hash;
                fragment.onchange();
            }
        </script>
        <style>.hide { display: none; }</style>
    </h:head>
    <h:body>
        <h:form id="processFragment" class="hide">
            <h:inputText id="fragment" value="#{bean.fragment}">
                <f:ajax event="change" execute="@form" listener="#{bean.processFragment}" render=":showFragment" />
            </h:inputText>
        </h:form>
        <p>Change the fragment in the URL. Either manually or by those links:
            <a href="#foo">foo</a>, <a href="#bar">bar</a>, <a href="#baz">baz</a>
        </p>
        <p>Fragment is currently: <h:outputText id="showFragment" value="#{bean.fragment}" /></p>
    </h:body>
</html>

这是适当的bean应该看起来的样子:
package com.stackoverflow.q3475076;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.event.AjaxBehaviorEvent;

@ManagedBean
@RequestScoped
public class Bean {

    private String fragment;

    public void processFragment(AjaxBehaviorEvent event) {
        // Do your thing here. This example is just printing to stdout.
        System.out.println("Process fragment: " + fragment);
    }

    public String getFragment() {
        return fragment;
    }

    public void setFragment(String fragment) {
        this.fragment = fragment;
    }

}

就这些了。

请注意,onhashchange事件是相对较新的,不受旧版浏览器的支持。在缺乏浏览器支持(未定义等)的情况下,您可以使用setInterval()以一定时间间隔检查window.location.hash。上面的代码示例至少应该给出一个良好的起点。它适用于至少FF3.6和IE8。


1
哇,那真是太棒了,谢谢分享。现在我需要看一下 JSF 2。 - clacke
1
对于IE6/7浏览器,请考虑使用jQuery Hashchange插件。另请参见http://stackoverflow.com/questions/5911899/ie6-7-back-forward-button-dont-change-window-location-hash - BalusC

3
以下是关于从语法上有效的URL / URI中提取片段的最可靠方法:

以下是从语法上有效的URL / URI中提取片段的最可靠方法:

 URI uri = new URI(someString);
 String fragment = uri.getFragment();

你如何将这个注入到bean中,取决于你使用的服务器端框架以及你是使用XML或注释进行注入,还是以编程方式进行注入。


谢谢你的想法。我正在寻找一种方法,在页面重新加载(GET)时捕获URL,然后执行getFragment(),正确解析以获取值,然后设置相应的变量。 - Choesang
方法存在那里没关系,但浏览器不会将片段信息发送到服务器。可以通过记录HTTP流量来查看。 - pcjuzer
1
虽然从技术上讲是正确的,但这个答案在实际和基本上是错误的。由 webbrowser 设置的片段不会到达 webserver。 - BalusC
好观点。但是,如果您将其编码并将其作为查询参数发送到不同的URL,则可以发送带有片段的URL。这就是许多注释和书签工具的工作方式。 - Stephen C

1

片段无法从服务器端看到,只能通过客户端脚本访问。通常的做法是,服务器端生成一个非参数化页面,然后根据片段参数由脚本进行修改。脚本可以使用带有查询参数的 AJAX 请求,其中 AJAX 响应由 JSF 生成,使用由参数控制的 bean。

如果您绝对希望在呈现页面本身时,服务器端可以访问片段参数,则需要使用参数作为查询参数重新加载页面。

编辑:要重新加载页面,您可以使用此代码:

if (window.location.hash != '') {
  var newsearch = window.location.search;
  if(newsearch != '') {
    newsearch += '&';
  }
  newsearch += window.location.hash.match(/#?(.*)/)[1];
  window.location.hash = '';
  window.location.search = newsearch;
}

我看到 Facebook 使用的技术,即在页面重新加载时将片段参数转换为查询参数。我想尝试这个想法。如何将包含片段的 URL 转换为带有查询参数的 URL 并在页面重新加载时实现呢? 例如:http://url.com/#a=1&b=2 -> http://url.com/?a=1&b=2 - Choesang
添加了示例代码。然而,我建议您编写代码来解析参数并进行适当的AJAX请求以重构已标记状态。此外,我的代码示例对您的需求来说过于简单,它不会检查例如重复的参数,因此当用户开始点击并且您再次向片段添加内容时,它将遇到问题。 - clacke
代码示例很笨重。只需使用window.location.hash即可获得片段,而window.location.search则是查询字符串。请阅读window.location文档。 - BalusC
你读的是旧版本的例子。在你评论之前,我已经纠正了它。 - clacke
是的,这一定是一个打开时间太久的浏览器标签页 :) - BalusC

1

观看这个问题,特别是答案:JSP Servlet anchor

  • 片段在服务器端无法看到。
  • 您可以在客户端提取片段,将其转换并通过Ajax发送到服务器。

非常好的参考资料。特别是 ajaxpatterns 的链接。 - clacke

0
非常感谢您的想法,尤其是 BalusC(向您致敬)。我的最终解决方案有点变异,我想与大家分享。
由于我正在使用 richfaces 3.3.3,就像使用 jsf1.2 一样,因此没有 。
在传统情况下,您应该单击带有 href 指向内部链接 的链接。我正在使用许多预打包组件,其中并非每个可点击项都是链接 。它可以是任何 HTML 元素。使用 Ben Alman 的可书签化 jQuery 插件非常有用。
请注意,在下面的代码中,我可以在 onclick 中以编程方式设置片段的值为键/值,并且在以下代码中,我将旧值替换为新值 jQuery.bbq.pushState(state,2)(或者我可以简单地在旧值后面添加 jQuery.bbq.pushState(state))。
在我的情况下,ws->md->nd 是一种分层数据,只需要其中一个并且其父级在服务器上计算,因此我用子值替换了父值。
<ui:repeat value="#{workspaceController.modulesForWorkspace}" var="item">  
 <a4j:commandLink onclick="var state = {}; state['md'] = '#{item.uuid}';
  jQuery.bbq.pushState( state, 2 );"    
  action="#{workspaceController.setSelectedModule}"
  reRender="localesList" 
  oncomplete="selectNavigationElement(this.parentNode>
    <f:param name="selectedModule" value="#{item.uuid}"/>
  </a4j:commandLink>
</ui:repeat>

在RichFaces中,这是一种很酷的方式,可以通过参数从浏览器调用setter方法。请注意,sendURLFragment被视为具有5个参数的JS方法,并将值传递到服务器(非常酷)。
<h:form id="processFragment" prependId="false" style="display: none">
  <h:inputText id="fragment" value="#{workspaceController.selectedWorkspace}">                                  
   <a4j:support event="onchange"/>
  </h:inputText>    
<a4j:jsFunction name="sendURLFragment" reRender="workspaces">
 <a4j:actionparam name="ws" assignTo="#{workspaceController.selectedWorkspace}"/>
 <a4j:actionparam name="md" assignTo="#{workspaceController.selectedModule}"/>
 <a4j:actionparam name="nd" assignTo="#{workspaceController.selectedContentNode}"/>
 <a4j:actionparam name="ld" assignTo="#{workspaceController.selectedLOD}"/>
 <a4j:actionparam name="ln" assignTo="#{workspaceController.contentNodeTreeLocale}"  
 converter="localeConverter"/>                              
</a4j:jsFunction>
</h:form>

当你复制粘贴新的URL并加载页面时,执行该操作。

window.onload = function(){
if(window.location.hash != ""){  
    //Parse the fragment (hash) from a URL, deserializing it into an object            
    var deparamObj = jQuery.deparam.fragment();
    var ws= '', md= '', nd= '', ld= '', ln = '';            
    jQuery.each(deparamObj, function(key, value) {              
        switch(key){
            case 'ws':
                ws = value;
                break;
            case 'md':
                md = value;
                break;
            case 'nd':
                nd = value;
                break;
            case 'ld':
                ld = value;
                break;
            case 'ln':
                ln = value;
                break;
            default:
                break;          
        }

    });
    sendURLFragment(ws,md,nd,ld,ln);
}   };  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接