首頁 / CompScience / Programming / Languages / JavaScript / 從Perl中使用Ajax

從Perl中使用Ajax

這一篇Using Ajax from Perl是2006年由Dominic Mitchell所撰寫,ㄚ琪試著把它翻成中文讓各位看看!

即使你間接地跟網站開發有點關聯,你不可能沒有聽到再過去的一年Ajax的一些問題,這可能聽起來像最熱門的詞彙而且之後會深陷在詳細研究中,儘管它無疑地是時髦的詞彙,但它也是相當有用的。

Ajax代表 “非同步的JavaScript和 XML”,它是Jesse James Garret在”Ajax: A New Approach to Web Applications“這篇文章裡所創造出來的名詞,請忽略這個足球隊,他們是冒充的。 😉

那真正的意義是什麼?總之,它是如何使您的網頁更具互動性,Ajax的核心技術是讓你更新你的部份網頁卻不用從頭開始載入所有的網頁,這啟用了一些很酷的效果,很多人會舉出Google MapsGmail 這個你有在用的偉大的例子,但是我喜歡的Ajax的部份是Flickr的那一部份。

當你登入到Flickr的時候,你可以看到你剛上傳的相片,在這個例子中,這張照片是我外出騎腳踏車時拿我的相機快拍的,不幸地,手機給了預設的名稱,而這樣是相當不詳細的(圖一)。

圖一 原來Flickr中的相片

假如你點擊標題,它會變成亮度反白的文字輸入欄位(圖二)。

圖二 編輯文字標題

把它變得有點意義一下,按Save按鈕,在變回原來的格市前,螢幕會告訴你正在做什麼(圖三)。

在螢幕的背後,Flickr使用JavaScript來對伺服器產生一個獨立的回呼並更新資料,它這樣做是通過一種叫做XMLHttpRequest來做的,在你探索Ajax的時候你會聽到很多關於XMLHttpRequest的事。

Flickr不必到另一個編輯頁面就可以讓你編輯相片的很多資料,這是很簡單的加強,卻可以讓整個應用程式更容易使用,這是值得讓大家認識這類的各式各樣技巧,以豐富您自己的應用程式,另外值得提出的是他們都是成市的加強,假如你把JavaScript停用,Flickr還是會讓你編輯這個相片的資料,你只是必須使用一個單獨的編輯螢幕來跟伺服器往返地溝通,使用Ajax讓你簡化你的用戶使用經驗。

那麼所有這混亂的JavaScript又如何?難道幾年前我們就擺脫了嗎?嗯,是的,這種看起來像Perl Golf的JavaScript早已甩掉包袱走了,我敢肯定這就跟你在發現Perl 5的模組如何運作前,你會對幾年前你所寫的Perl 4指令碼有相同的感覺,現代的JavaScript是不同的猛獸,它相當地標準,所以過去跨瀏覽器的問題已經不是沒有就是變少了,另外也有一個重點使JavaScript盡可能地不突兀,所有這一切的變化,甚至有嘗試要將瀏覽器中的JavaScript重新命名為DOM指令碼

Ajax有幾個部份:

  • 非同步:要確保在它發生的時候任何活動不會鎖住瀏覽器。
  • JavaScript:處理web瀏覽器內的網頁。
  • XML:從伺服器回傳資料。
    :

    你可能花很多時間找出所有部分在客戶端的JavaScript和在伺服端的Perl,以解決如何在程式碼中使用Ajax,然而,這是Perl;我們喜歡有點懶惰,幸虧,已經在CPAN上有了一個模組把痛苦給帶走:CGI::Ajax

    CGI::Ajax 對你的CGI程式提供了一些基礎結構,你告訴它你的一些函式,它就設定好JavaScript然後呼叫它們並且傳回結果到你的頁面,你不需要擔心寫JavaScript程式碼來做這件事,因為CGI::Ajax會照顧好這件事,你所需要做的是新增一些JavaScript呼叫給你指令碼中定義的函式並且讓CGI::Ajax處理這些管路。

    CGI::Ajax在JavaScript所建構的函式或多或少遵循著相同的模式,他們需要兩個參數:a list of HTML ID列表來取得輸入的表單,以及HTML ID的第二個列表來新增插入的結果,在你的HTML中有ID屬性是啟用這個行為的先決條件,CGI::Ajax處理輸入值來查詢你的網頁,然後新增從伺服端傳回來的結果。

    透過你CGI指令碼內的函式製作來提供給瀏覽器,你有能力來做你通常不會作的事,舉個例來說,你查詢資料庫一些值或是查詢系統平均負載,任何你可以在Perl做的但卻不能在JavaScript裡做的現在都變成可能。

    檢查使用者名稱

    要探索CGI::Ajax,想一想典型的問題,你有一個註冊的網頁程式,你必須輸入使用者名稱跟密碼來註冊這個應用程式,因為這是一個很受歡迎的程式,你要的使用名稱可能已被使用,不幸的是,你必須重新執行整個表單然後在告訴你不能有這個使用者名稱fluffykitten之前來等候伺服器來收到它,那CGI::Ajax如何來幫助你解這個問題呢?

    開始做一個基本的CGI指令碼來處理註冊,我會用最小的程式以盡力專注在手頭上的這個問題,一旦你可以執行這程式你可以隨意地多加一些功能,我會詳細解釋下面整個指令碼,但你也可以下載這個註冊的指令碼

    開始的時候它像所有好的Perl程式碼,但是使用strictwarnings 模組,緊接著是唯一要用的CGI.pm 模組,它會建立一個新的CGI物件然後呼叫 main()來工作。

    #!/usr/local/bin/perl
    # User registration script.
    use strict;
    use warnings;
    
    use CGI;
    my $cgi  = CGI->new();
    main();

    大部分的main()處理HTML的部份,對於實際的指令碼,使用像是HTML::TemplateTemplate Toolkit來取代直接在指令碼中使用HTML。

    最有趣的事發生在中間這段,首先它檢查傳來的user參數,如果是這樣,程式碼檢查是否user參數是好的,然後紀錄username在我們的資料庫中,假如有任何問題,它也會提到那些,最後,它會送出建好的HTML到瀏覽器來顯示。

    sub main {
        my $html = <<HTML;
    <html><head>
    <title>Ordinary CGI demo</title>
    </head><body>
    <h1>Signup!</h1>
    HTML
        if ( my $user = $cgi->param('user') ) {
            my $err = check_username( $user );
            if ( $err ) {
                $html .= "<p class='problem'>$err</p>";
            } else {
                save_username( $user );
                $html .= "<p>Account <em>$user</em> created!</p>\n";
            }
        }
        my $url = $cgi->url(-relative => 1);
        $html .= <<HTML;
    <form action="$url" method="post">
    <p>Please fill in the details to create a new Account.</p>
    <p>Username: <input type="text" name="user" id="user"/></p>
    <p>Password: <input type="password" name="pass" id="pass"/></p>
    <p><input type="submit" name="submit" value="SIGNUP"/></p>
    </form></body></html>
    HTML
        print $cgi->header();
        print $html;
    }

    為了簡單地直行一個使用者資料庫,我決定儲存使用者名單在一個文字檔中,每一行一個名字,要檢查使用者名稱是否已被使用,程式碼讀取檔案然後將每一行與傳入的值做比較,不分大小寫,假如有任何問題,他們會傳回一個字串,假如檔案不存在,那麼程式碼允許任何使用者名稱(這幫助來避免你第一次執行這個指令碼),再一次,實際的程式可能會使用DBI來儲存使用者資料在資料庫中。

    sub check_username {
        my ( $user ) = @_;
        return unless -f '/tmp/users.txt';
        open my $fh, '<', '/tmp/users.txt'
          or return "open(/tmp/users.txt): $!";
        while (<$fh>) {
            chomp;
            return "Username taken!" if lc $_ eq lc $user;
        }
        return;
    }

    最後,為了儲存一個用戶名,程式碼必須把它加到檔案的最後一行。

    sub save_username {
        my ( $user ) = @_;
        open my $fh, '>>', '/tmp/users.txt'
          or die "open(>>/tmp/users.txt): $!";
        print $fh "$user\n";
        close $fh;
        return;
    }

    現在你應該有一個指令碼讓你輸入使用者名稱並且紀錄到一個檔案中,假如你試著輸入相同的名字兩次,它會阻止你,就像Hotmail一樣,現在想像一下,就像Hotmail一樣,你也必須花些時間來做一個captcha影像,那麼當你最後管理那些很奇怪的波浪線表示時,你只要按下Submit按鈕就會被告知你輸入的使用者名稱沒有影響,然後,你要考慮的另一個用戶名,並嘗試破解很奇怪的另一行,你會知道儘快告訴使用者使用者名稱不能用是很重要的。

    輸入 CGI::Ajax

    現在做一些小小的變動,你可以使用Ajax的指令碼來檢查使用者名稱是否有效,那個方法就是任何問題可以馬上顯示出來,這沒有很大的變動,在指令碼前頭,載入CGI::Ajax模組然後建構一個新的物件,同時註冊函式check_username()可以透過Ajax來呼叫,之後直接取代呼叫main(),呼叫build_html(),傳入main()的參考,這是CGI::Ajax如何運做的一個重要部份,它給予CGI::Ajax能力來攔截它需要的正常控制流,你也可以下載有Ajax的登錄程式碼

    #!/usr/local/bin/perl
    # User registration script.
    
    use strict;
    use warnings;
    
    use CGI;
    use CGI::Ajax;
    
    my $cgi  = CGI->new();
    my $ajax = CGI::Ajax->new( check_username => \&check_username );
    print $ajax->build_html( $cgi, \&main );

    在main()函式唯一結構的改變,取代了印出標頭跟產生的HTML,現在它傳回內容。

    sub main {

    # …

    # print $cgi->header();

    # print $html;

    return $html;

    }

    在那之後,CGI::Ajax會送出內容到你的瀏覽器。

    在處理Ajax的伺服端之後,現在回來看看客戶端,客戶端需要CGI::Ajax提供的良好功能,要這樣做,你需要一些JavaScript,假如你看了第二隻指令碼,你會看到CGI::Ajax已經插入某些在<head>這個區塊內的JavaScript,這是你公開Perl函式給JavaScript的程式碼,所有的事件就是連接發生在那些公開的Perl函式的事件。

    假如你之前有用過JavaScript,你可能要想像onchange的屬性,那是正確的想法(在使用者名稱欄位變動時觸發Ajax呼叫),但是做這事的理想方法,因為它會干擾,還有實在沒有必要要在HTML中有JavaScript,另一個作法可以建立一個小檔案來繫結標記語言到公開的Perl函式(下載binding.js )。

    開始的時候它啟動Simon Willison寫的addLoadEvent函式,這個函式在頁面完成載入時會使用一段JavaScript並且執行,有用的是你可以不只一次地呼叫它也不會有不良的影響,你可以使用window.onload來直接處理,但是這得移除先前使用它的程式碼,如果到處使用addLoadEvent(),這會是一個問題。

    // Run code when the page loads.

    function addLoadEvent(func) {

    var oldonload = window.onload;

    if (typeof window.onload != ‘function’) {

    window.onload = func;

    } else {

    window.onload = function() {

    oldonload();

    func();

    }

    }

    }

    接下來的JavaScript做了實際的工作,雖然在做任何事之前,含有一個小小地檢查來確認瀏覽器是可以處理所有這些的Ajax,藉著沒有含括號的瀏覽器函式的名稱(document.getElementById),JavaScript會傳回一個參考,假如這個函式不存在,它會傳回null,假如這個函是不存在,這個程式碼只會直接傳回,導致這一個頁面沒有Ajax功能,這是優美的退化,如果加強的行為不能運作它會讓普通的行為發生。

    當程式碼知道它的執行安全時,它會針對我們輸入的username元素來查詢文件,假如有找到,程式碼只要每次username欄位有改變就會安排呼叫check_username()函式,在文件裡有兩個參數是ID的列表,第一個參數它的值會傳到伺服器的check_username(),第二個包含ID的參數會從函式那裡新增傳回的值。

    // Set up functions to run when events occur.
    function installHandlers() {
      if (!document.getElementById) return;
      var user = document.getElementById('user');
      if (user) {
          // When the user leaves this element, call the server.
          user.onchange = function() {
              check_username(['user'], ['baduser']);
          }
      }
    }

    installHandlers()定義下,所有要保留的就是確保頁面載入時它會真正執行。

    addLoadEvent( installHandlers );

    binding.js 完成的時候,你需要將兩個小改變弄到產生的HTML中,首先,含括一個指令碼script標籤來真正載入它:

    <script type="text/javascript" src="binding.js"></script>

    第二,建立一個id為baduser的元素來新增結果,我建立一個空白的強調標籤在username欄位後面。<p>Username: <input type="text" name="user" id="user"/>

    <em id="baduser"></em></p>

    在那個地方,你應該能夠註冊一個使用者名稱,然後當你試著註冊兩次的時候看到它失敗,看看username無效的時候這個標籤如何提醒。

    Inside CGI::Ajax

    現在你知道如何使用CGI::Ajax來動態更新你的網頁, 究竟裡面有什麼變化讓它可以這樣運作?你已經看到了check_username()的JavaScript版本來收集input欄位的值並將值傳給你原來的CGI指令碼,你可以看見當你在建構CGI::Ajax物件後多加一行到指令碼時所發生的情形。

    $ajax->JSDEBUG(1);

    在那個地方,CGI::Ajax會紀錄每個到伺服器的呼叫在你的網頁下面。在我的伺服器上,它看起來像這樣:

    http://localhost/~dom/cgi-ajax/ajax.cgi?fname=check_username&args=dom&user=dom

    假如你按這個連結,你會看到它傳回你所用的字串Username 'dom',不會有別的資料,它幾乎忽略了程式的大半部份而只送出check_username()的結果,當程式的主要部份呼叫ajax->build_html(),CGI::Ajax檢查一個叫做fname的參數存在,假如有找到,那麼它會檢查看看函式是否有註冊,假如有就呼叫,傳args的參數,然後傳回一個單一的函式回去給瀏覽器,完全避開主程式。

    真實世界的CGI::Ajax

    Ajax是一個工具,就像很多其他的人已經在Web上設計程式那樣可用,它有一些比較適合或不適合使用的地方,就像HTML中的table元素一樣,我選擇username驗證作為一個範例是因為我認為它是Ajax可以真正用來增加一個功能到現有的Web應用程式中的一個很好的例子,使用Ajax來增強表單,特別是很常且複雜的那種,能夠創造奇蹟的使用,當然也有其它的地方,就像我在開始的時候指出Flickr,行內編輯是一個偉大的福音。

    像所有的工具那樣,知道什麼時候不用跟什麼時候可用一樣重要,像CGI::Ajax的套件使它非常便於使用Ajax,所以你必須保持克制,加入少許的Ajax就可以讓它更適合地重新載入整個頁面真是太簡單了,假如你發現你正使用Ajax呼叫來更新網頁的大部分,那麼它很可能不值得你麻煩Ajax了,真的,最好的方針是做一些可用性測試,想想你的使用者如何與你的應用程式互動,什麼是最好的方式可以讓你幫助他們實現他們正在嘗試做的?

    有一些很好的資源可以用來決定什麼時候可用什麼時候不用Ajax,最完整的似乎是Ajax Patterns,有些時候 可以看看歐萊禮的書,有一篇來自Alex Bosworth的部落格文章提供了他的意見在Ten Places You Must Use Ajax裡 (即使真的只有6點而已), Alex也有一個好的作品在”Ajax Mistakes”,也值得去注意看看。

    也有一些技術上的原因來考慮什麼時候在你的應用程式裡執行Ajax,你必須意識到任何使用者都可以存取你正曝露的伺服端函式,即使你可能會認為他們只是內部的程式而已,你的應用程式突然有一個API,基於安全或效能的緣故,你可能想要重新考慮你所曝露的(雖然這個建議也同樣適用於你應用程式中的一般網頁),還要注意你從CGI::Ajax取得的API跟你的應用程式內部的緊密結合,假如你改變了一個曝露的函式名稱,你必須改變API,假如API是一個很大的正在進行中專案的一部分,你可能想要考慮花些時間來看看更多的REST-之類的介面來替代,這些往往容易跟其他語言的程式設計師來工作。

    現在你的站台真的有了API,你可能想要考慮文件化來說明它如何運作,所以你的使用者可以用這個API來建構,這就是Flickr已經做過的偉大影響,那些使用Flickr的人們從沒有想過有新的工具,只因為他們讓他們的使用者可以存取API。

    不要讓這一切阻止你;你已經看過了要添加一些閃光到你的應用程式中是如何地簡單,想想如何讓用戶的生活更簡單吧。

Print Friendly, PDF & Email
馬上成為工作達人的Fans

About ㄚ琪

工作達人Fun Taiwan的創辦者及總編,可以在這裡更認識他。

發表迴響

你的電子郵件位址並不會被公開。 Required fields are marked *

*

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料

Scroll To Top