weidagang2046的專欄

          物格而后知致
          隨筆 - 8, 文章 - 409, 評論 - 101, 引用 - 0
          數(shù)據(jù)加載中……

          服務(wù)器端可控情形的Javascript跨域訪問解決方法

          2006-12-5

          ?

          我在最近的一個(gè) web 項(xiàng)目中為了實(shí)現(xiàn) bookmark 功能碰到了 javascript 跨域訪問的問題。起初,在 google 上搜的很多解決方案并不適用于我的情形,只在有一篇文章中提到的遠(yuǎn)程加載 javascript 方法從理論上看到了解決的希望。但可惜作者只是一筆帶過,并未用例子詳細(xì)說明,所以不得不摸索了一陣才把這個(gè)問題搞定。在此,希望通過本文為同樣被這個(gè)問題困擾的朋友們提供一個(gè)解決方法作為參考。如有錯(cuò)誤,歡迎指正!

          ?

          Bookmark 是目前許多 web2.0 網(wǎng)站 (http://del.icio.us/, www.diigo.com 等)都提供的熱門 feature 。它能將互聯(lián)網(wǎng)上自己喜歡的網(wǎng)頁收藏到 Bookmark 服務(wù)器上。本文要解決的問題就發(fā)生在用戶提交網(wǎng)頁 URL (還包括 Tag, Notes 等)給 Bookmark 服務(wù)器時(shí)。

          ?

          關(guān)于 URL 的提交至少可以有三種方式:

          ?

          1.?????? 登陸 Bookmark 服務(wù)器的提交頁面,將要收藏的 URL 通過該頁面提交給服務(wù)器。

          2.?????? 安裝瀏覽器插件,通過插件將 URL 提交給服務(wù)器。

          3.?????? Bookmark 服務(wù)器動態(tài)加載 javascript 小工具到當(dāng)前頁面,通過它來完成提交工作。(參考 diigo 的例子,收藏一個(gè)網(wǎng)頁鏈接到瀏覽器收藏夾,鏈接的 URL 是一段 javascript 代碼,它會從服務(wù)器加載一段 javascript 注入當(dāng)前網(wǎng)頁)

          ?

          第一種方式開發(fā)起來最簡單,但對用戶來講比較麻煩,每次都需要先登陸 Bookmark 服務(wù)器才能完成提交;第二種方式我并不熟悉插件開發(fā),而且用戶也不喜歡太多的插件堆滿自己的瀏覽器;第三種方式開發(fā)難度小,又避免了每次登陸服務(wù)器的麻煩,所以我最終采用了它。

          ?

          上面講的第三種方式中動態(tài)加載的 javascript 小工具除了需要生成 UI 供用戶填寫信息( URL tag notes 等),當(dāng)用戶點(diǎn)擊提交的時(shí)候,還要完成與服務(wù)器通信的功能。在沒有跨域訪問經(jīng)驗(yàn)的情況下,最先想到的當(dāng)然是 ajax !但很快就會發(fā)現(xiàn)根本行不通。

          ?

          跨域訪問,簡單來說就是 A 網(wǎng)站的 javascript 代碼試圖訪問 B 網(wǎng)站,包括提交內(nèi)容和獲取內(nèi)容。由于安全原因,跨域訪問是被各大瀏覽器所默認(rèn)禁止的。寫過跨域訪問 ajax 的朋友相信都遇到過被告知“沒有權(quán)限”的情況。通過 XMLHttp 來發(fā)送數(shù)據(jù)給 Bookmark 服務(wù)器的嘗試失敗了。于是,看到網(wǎng)上的一些資料,我又開始嘗試用 javascript 小工具在用戶網(wǎng)頁動態(tài)創(chuàng)建一個(gè)隱藏的 iframe, iframe src 指向服務(wù)器的一個(gè) servlet ,試圖通過調(diào)用 iframe 中提供的 javascript 來完成與服務(wù)器的通信。但不幸的是,用戶網(wǎng)頁中的 javascript 代碼訪問 iframe 也被瀏覽器歸為跨域訪問(特指 iframe src 指向其它網(wǎng)站的情形),嘗試再次失敗。

          ?

          最終,在一篇文章中看到,與 iframe 不同,如果 A 網(wǎng)站從 B 網(wǎng)站加載 javascript A 網(wǎng)站可以自由的訪問該 javascript 的內(nèi)容,并不會被瀏覽器認(rèn)為是跨域訪問。模仿剛才 iframe 的思路,當(dāng)用戶點(diǎn)擊提交時(shí),可以動態(tài)創(chuàng)建一個(gè) javascript 對象,該對象的 src 指向 Bookmark 服務(wù)器的一個(gè) servlet ,注意: URL Tag Notes User Password 等信息被作為 src URL 參數(shù)傳給服務(wù)器。請看下面的代碼:

          ?

          var url = "http://localhost:8080/Deeryard/BookmarkServlet?" +

          ?????????? "url=" + url_source + "&" + "title=" + title + "&" +

          "tag=" + tag + "&" + "notes=" + notes + "&" + "user=" + user + "&" + "password=" + password;

          ?

          url = encodeURI(url);

          ???

          //Submit to server with a trick

          var js_obj = document.createElement( "script" );

          js_obj.type = "text/javascript" ;

          js_obj.setAttribute( "src" , url);

          ???

          //Get response from server by appending it to document

          document.body.appendChild(js_obj);

          ?

          上面例子中, js_obj.setArrribute() 將信息作為 src URL 參數(shù)提交給了 Bookmark servlet 。那么用戶又如何取得服務(wù)器的響應(yīng)信息呢?答案就是最末一行代碼, servlet 的輸出必須是 javascript 代碼,它可以調(diào)用用戶網(wǎng)頁上的其他 javascript 函數(shù),以及操作 dom 對象。下面的 servlet 代碼生成了一個(gè) javascript 函數(shù)調(diào)用:

          ?

          out.write("onServerResponse(INADEQUATE_INFORMATION);");

          ?

          document.body.appendChild(js_obj) 執(zhí)行后 onServerResponse( INADEQUATE_INFORMATION) 就會得到執(zhí)行,使客戶網(wǎng)頁響應(yīng)服務(wù)器結(jié)果。這樣一個(gè)完整的通信過程就完成了。

          ?

          來總結(jié)一下這個(gè)案例,首先與很多跨域訪問的情形不同,本文提到的跨域訪問需要對服務(wù)器端進(jìn)行控制,即讓服務(wù)器端 Servlet 來適應(yīng)客戶端網(wǎng)頁 javascript 的需求;而其他一些常見的例子則對服務(wù)器端沒有控制能力,比如從其他網(wǎng)站抓內(nèi)容的小偷程序。另外,需要注意的是這種方法中實(shí)際用到了 get 方法來提交信息,從一些資料上看到, get 方法每次提交的信息不能超過 2k

          posted on 2006-12-05 22:48 weidagang2046 閱讀(9284) 評論(11)  編輯  收藏

          評論

          # re: 服務(wù)器端可控情形的Javascript跨域訪問解決方法  回復(fù)  更多評論   

          IBM developerWorks上面有個(gè)文章,里面詳細(xì)講了ajax的三種數(shù)據(jù)傳輸機(jī)制,你上面這個(gè)實(shí)例就是其中的第三種方法。
          教程文章地址:
          http://www-128.ibm.com/developerworks/cn/views/xml/tutorials.jsp?cv_doc_id=110100
          需要注冊個(gè)ID才能閱讀。
          2006-12-06 09:27 | xinheqishi

          # re: 服務(wù)器端可控情形的Javascript跨域訪問解決方法  回復(fù)  更多評論   

          我有個(gè)問題哦....
          比如,我第一次發(fā)送的時(shí)候創(chuàng)建一個(gè)script對象,然后在很斷的時(shí)間內(nèi)發(fā)送了第二次,也就是創(chuàng)建了第二個(gè)script對象.那天他就把第一個(gè)覆蓋了,第一個(gè)怎么辦??

          但我又不能創(chuàng)建很多個(gè)script對象,那樣很占資源,只能刪除一些對象,那樣就又可能刪除寫沒有反會的對象......

          我的前提是我則個(gè)情求比較頻繁..

          希望能解我之惑...
          email: zuan8888@gmail.com
          2007-01-13 14:37 | 剎那

          # re: 服務(wù)器端可控情形的Javascript跨域訪問解決方法  回復(fù)  更多評論   

          創(chuàng)建javascript對象的時(shí)候指定id屬性可以避免覆蓋;也可以按id查找對象來判斷是否已經(jīng)返回,如果找到的話就可以看情況刪除。關(guān)于id的維護(hù)問題辦法應(yīng)該很多。比如,維護(hù)一個(gè)全局計(jì)數(shù)器。

          如果一定要同步的話(比如,第一次返回才能發(fā)第二次),建議看看javascript的wait()函數(shù)。

          ps: 我的javascript經(jīng)驗(yàn)也不多,上面的建議還需要實(shí)踐檢驗(yàn)。歡迎交流!
          2007-01-13 18:05 | weidagang2046

          # re: 服務(wù)器端可控情形的Javascript跨域訪問解決方法  回復(fù)  更多評論   

          請問這種方式下客戶端(瀏覽器端)和Bookmark servlet端之間的關(guān)系是不是和直接通過瀏覽器訪問Bookmark servlet的關(guān)系是一樣的? 因?yàn)槲蚁胗眠@個(gè)方法來解決一個(gè)問題,A網(wǎng)站(應(yīng)該是相當(dāng)于這里的Bookmark servlet)負(fù)責(zé)控制邏輯,B網(wǎng)站負(fù)責(zé)頁面的外觀顯示.
          客戶端從B網(wǎng)站進(jìn)入之后, 要?jiǎng)討B(tài)創(chuàng)建javascript對象,提交多次請求, 而我想維護(hù)多個(gè)請求之間的狀態(tài)關(guān)系;是否和直接通過瀏覽器訪問B網(wǎng)站一樣,維持session就OK了?

          希望能解我之惑....謝謝!
          2007-01-13 19:28 | sparklet

          # re: 服務(wù)器端可控情形的Javascript跨域訪問解決方法  回復(fù)  更多評論   

          因?yàn)锽ookmark Servlet對javascript一無所知,只當(dāng)成一般的網(wǎng)頁文本返回,所以我認(rèn)為和一般瀏覽器的訪問沒有區(qū)別,用session維護(hù)狀態(tài)是完全可以的。
          2007-01-13 19:39 | weidagang2046

          # re: 服務(wù)器端可控情形的Javascript跨域訪問解決方法  回復(fù)  更多評論   

          可行 跨域真是雙刃劍
          2007-02-04 11:42 | 小雨

          # re: 服務(wù)器端可控情形的Javascript跨域訪問解決方法[未登錄]  回復(fù)  更多評論   

          可以MSN交流嗎?tangcaiyou@hotmail.com
          2007-06-17 10:45 | eric

          # re: 服務(wù)器端可控情形的Javascript跨域訪問解決方法  回復(fù)  更多評論   

          我嘗試了類似的代碼卻沒有成功。
          我在一個(gè)A網(wǎng)站的portlet中,iframe了一個(gè)B網(wǎng)站的jsp頁面,當(dāng)點(diǎn)擊該jsp的某個(gè)鏈接后,我需要將一個(gè)參數(shù)傳送到A網(wǎng)站的某Servlet,可是沒有成功。
          代碼如下:
          site A的portlet:
          從IFrame中的頁面?zhèn)鱽淼膮?shù):<%=portlet_a_s%>
          <br/>
          <iframe width="300" height="300" src="http://siteB:7001/myWeb/B.jsp">
          </iframe>

          site B的jsp:
          <%@ page language="java" contentType="text/html;charset=UTF-8"%>
          <html>
          <head>
          <title>
          inner page
          </title>

          <script type="text/javascript">
          var lastScript;
          var h=document.getElementsByTagName("head")[0];

          function loadScript(url){
          var f=document.createElement("script");
          var d=new Date().getTime();
          f.type="text/javascript";
          f.id=d;
          f.src=url+'&date='+d;
          document.body.appendChild(f);
          if(lastScript&&g(lastScript))g(lastScript).parentNode.removeChild(g(lastScript));
          lastScript=d;
          }

          function g(x){
          return document.getElementById(x)
          }

          function onClickMeasureId(id){
          var hidden = g("hidden_input");
          hidden.value = id;
          var url = "http://siteA:7001/testWeb/portlets/portlet_a/PortletAServlet?id="+id;
          loadScript(url);
          }

          function onResponse(){
          alert("response");
          };
          </script>
          </head>
          <body>
          <a onclick="onClick('參數(shù)1')" href="#">參數(shù)1</a>
          </body>
          </html>

          siteA的Servlet:
          public class PorletAServlet extends HttpServlet
          {
          public void service(HttpServletRequest request,HttpServletResponse response){
          if ( request.getParameter("id") != null ){
          String id = request.getParameter("id").toString();
          HttpSession session = request.getSession();
          session.setAttribute("measure_id",request.getParameter("id") );
          try{
          response.setContentType("text/javascript");
          PrintWriter out = response.getWriter();
          out.write("onResponse();");
          }
          catch(Exception e){}
          }
          }
          }

          我執(zhí)行siteA的portal,在siteA的servlet中加入斷點(diǎn),點(diǎn)擊portlet中iframe中的鏈接,發(fā)行servlet并沒有得到執(zhí)行。實(shí)在不知道為何不成功。

          不知道博主可否提供更加完整的例子?謝謝!
          2007-06-22 14:57 | liao

          # re: 服務(wù)器端可控情形的Javascript跨域訪問解決方法  回復(fù)  更多評論   

          @liao
          你的例子應(yīng)該沒有大問題,應(yīng)該調(diào)查servlet沒有得到執(zhí)行的原因。可以把url單獨(dú)拷貝出來,敲到瀏覽器的地址欄中試試看servlet能否執(zhí)行。我估計(jì)是url本身的問題,很可能沒有真正對應(yīng)到那個(gè)servlet。
          2007-06-22 16:31 | weidagang2046@gmail.com

          # re: 服務(wù)器端可控情形的Javascript跨域訪問解決方法  回復(fù)  更多評論   

          你好我 我碰到了一個(gè)問題

          和你的說的類似

          希望你幫我看下

          多謝了 http://so.hr33.com/SearchResult.aspx?key=a&companyname=&zw=100&dq=100&sj=90&xl=10&jy=0&sex=2&age=0&yx=0&xz=0&hy=0&RCount=29&Page=1&type=1

          你點(diǎn)申請后點(diǎn)關(guān)閉就會提示 "拒絕訪問.."

          MAIL 我 colvinliu@qq.com
          2007-12-20 17:06 | colvinliu

          # re: 服務(wù)器端可控情形的Javascript跨域訪問解決方法  回復(fù)  更多評論   

          轉(zhuǎn)了一大圈 終于在這里找到了解決方法
          2010-04-22 11:21 | 唐兵

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 襄樊市| 大方县| 梨树县| 麻江县| 呼玛县| 禹州市| 清新县| 济南市| 兴城市| 屏东市| 长兴县| 金湖县| 西和县| 罗甸县| 朔州市| 邹平县| 观塘区| 北川| 新建县| 长垣县| 西安市| 六枝特区| 大荔县| 广安市| 湘潭市| 太康县| 尉犁县| 吉林省| 锦屏县| 宜阳县| 林芝县| 汝城县| 行唐县| 措勤县| 嵊泗县| 昭通市| 明水县| 桓台县| 重庆市| 赤城县| 如东县|