AJAX và một số kỹ thuật liên quan - Phần 4

IV. Đối tượng XMLHttpRequest

AJAX là kết quả của một trò chơi ghép hình, trên sân chơi của những người tiên phong.

Có hai lý do khiến tôi liên tưởng AJAX với 1 trò xếp hình. Thứ nhất, AJAX là sự kết hợp của 2 mảnh ghép lớn : Java Script và XML. Thứ 2, về phía các ứng dụng Web - AJAX, cấu trúc của các trang luôn là 1 tổng thể của nhiều thành phần riêng biệt ghép lại, mà nội dung của mỗi thành phần có thể được cập nhật bất cứ lúc nào bạn muốn, trong khi không tác động đến những thành phần khác nằm cùng giao diện.

Vào năm 1993, Marc Andreessen và các cộng sự của anh ở Mosaic Communications Corporation đã sáng tạo ra Mosaic, trình duyệt đầu tiên trên thế giới. Cuối năm 1994, kết thúc thời gian thử nghiệm, Mosaic ra mắt cộng đồng với tên chính thức là Netscape Navigator. Netscape cũng là trình duyệt đầu tiên sử dụng Java Script và cookies.

Nhưng khi bước sang thế kỷ 21, trong lúc mà Netscape vẫn trung thành với công nghệ LiveConnect, Microsoft đã sớm nhận thấy khả năng tiềm ẩn của XML. Cùng với việc cho ra mắt công nghệ .Net Framework phía server, Microsoft cũng thêm vào phiên bản 5.0 của trình duyệt IE nhiều tính năng mới nhằm mở rộng khả năng xử lý dữ liệu XML cho phía client.

.Net Framework, còn được biết đến với tên NGWSF (Next Generation Web Services Framework), là một công nghệ dựa trên nền tảng XML, cho phép tạo ra các web services sử dụng giao thức SOAP để truyền tải dữ liệu, và triệu gọi lẫn nhau thông qua HTTP/HTTPS.

SOAP (Simple Object Access Protocol, hay Service Oriented Architecture Protocol) là một hình thức mới của POST trước đây, với phần dữ liệu được đóng gói theo khuôn dạng XML, cho phép mở rộng khả năng mô tả thông tin truyền tải, thay vì chỉ đơn thuần là các cặp name=value. SOAP đem dữ liệu được mô tả theo cú pháp của XML, ra vào các dịch vụ web qua cổng mặc định 80 của HTTP. Điều này giúp nó trở nên dễ được chấp nhận bởi firewall hơn so với các hình thức khác.

Ở thời điểm mới ra mắt, IE 5 là trình duyệt hỗ trợ XML đầy đủ nhất. Với IE, bạn có thể nhúng trực tiếp XML vào hồ sơ HTML bằng cặp thẻ XML. Nếu bạn cung cấp cho thẻ này 1 thuộc tính ID và gán giá trị định danh vào đó, bạn có một Data Island. File XML của bạn lúc này trở thành data source. Việc truy xuất dữ liệu từ data source để hiển thị ra trình duyệt có thể được thực hiện một cách đơn giản nhờ Data Island mà không cần đến ActiveX.

Trong ví dụ dưới đây, tôi có 1 file XML present.xml, nội dung như sau :
<?xml version="1.0"?>
<present>
  <author>Sacroyant Nguyen</author>
  <homePage>http://sacroyant.googlepages.com</homePage>
  <contactEmail> sacroyant@vninformatics.com </contactEmail>
</present>

Tôi nhúng nó vào 1 trang HTML bằng cặp thẻ XML có định danh data :
    
<xml id="data" src="present.xml"></xml>

Script sau sẽ truy xuất dữ liệu thông quan Data Island và hiển thị ra trình duyệt :

<script type="text/javascript">

function parseXML(){
    var oNode=data.documentElement;
  var str='';
  if(oNode.hasChildNodes()){
    for(var i=0;i<oNode.childNodes.length ;i++){
        str+='<b>'+oNode.childNodes[i].nodeName+'</b>';
        str+=" : <font color=blue>"+oNode.childNodes[i].text+"</font><br>";
    }
  }
  return str;
}

var p=parseXML();
    document.write(p);

</script>


Hàm parseXML có nhiệm vụ đi sâu vào trong cây hồ sơ XML, tìm kiếm các node thứ cấp để cuối cùng trả về 1 chuỗi bao gồm tên và giá trị text của tất cả các node bên dưới node gốc.

Kết quả trên IE 7 :



Việc xử lý XML trên IE chỉ yêu cầu rất ít nỗ lực như vậy. Nhưng trong cuộc chạy đua về công nghệ web này, không có kẻ nào dừng lại nhường bước cho đối thủ. Netscape về với AOL, mã nguồn của trình duyệt Netscape Navigator trở thành open source. Mozilla Foundation phát triển code này để cho ra đời FireFox Mozilla, một trình duyệt mà nói theo cách của Thomas Friedman trong cuốn "Thế giới phẳng", thì : đang "cưỡi nhẹ nhàng trên đỉnh Window".

Mozilla đưa FireFox rời khỏi LiveConnect để tập trung vào phát triển Java Script engine. Đây thực sự là một lựa chọn sáng suốt. Thừa hưởng nền Netscape, FireFox tự cho nó là chủ nhân của Java Script. Một loạt đối tượng mới được bổ sung vào nhân Java Script của FireFox, mà mục đích đầu tiên là tăng khả năng xử lý XML, sao cho bất cứ điều gì IE làm được thì FireFox cũng làm được, và những gì IE không làm được, FireFox chưa chắc đã không làm được.

Câu này nghe vui vui. Đây chúng ta chỉ nói về khả năng của Java Script trong trình duyệt. Bạn hãy thử chạy script sau :
<script>

function Pow(x) x * x;

var a=Pow(5);
document.write(a);

</script>


Cho đến thời điểm này chỉ có duy nhất 1 trình duyệt là Gran Paradiso, tức FireFox 3.0, hiểu được script trên và hiển thị kết quả 25.



Vì lẽ đơn giản, hàm Pow, tính bình phương của 1 số, trong script trên được viết theo syntax của Java Script 1.8. Để các browser cũ hiểu được, phải viết đầy đủ như dưới đây :

function Pow(x){
   return x * x;
}

Bạn sẽ không sai nếu gọi IE 7.1, last version của MSIE là 1 trình duyệt cũ !

Trở lại với các thành phần hỗ trợ thao tác XML trong FireFox, có 3 đối tượng quan trọng là : XMLSerializer, DOMParser và XMLHttpRequest.

XMLHttpRequest làm việc theo cách tương tự 1 ActiveX có tên là XMLHTTP trong IE. Interface của nó như dưới đây :


interface XMLHttpRequest {
  // event handler
          attribute EventListener onreadystatechange;

  // state
  const unsigned short UNSENT = 0;
  const unsigned short OPEN = 1;
  const unsigned short SENT = 2;
  const unsigned short LOADING = 3;
  const unsigned short DONE = 4;
  readonly attribute unsigned short readyState;

  // request
  void open(in DOMString method, in DOMString url);
  void open(in DOMString method, in DOMString url, in boolean async);
  void open(in DOMString method, in DOMString url, in boolean async, in DOMString user);
  void open(in DOMString method, in DOMString url, in boolean async, in DOMString user, in DOMString password);
  void setRequestHeader(in DOMString header, in DOMString value);
  void send();
  void send(in DOMString data);
  void send(in Document data);
  void abort();

  // response
  DOMString getAllResponseHeaders();
  DOMString getResponseHeader(in DOMString header);
  readonly attribute DOMString responseText;
  readonly attribute Document responseXML;
  readonly attribute unsigned short status;
  readonly attribute DOMString statusText;
};


Nhìn vào interface, bạn có thể nhận biết toàn bộ các thuộc tính và phương thức cơ bản của XMLHttpRequest. Vì đây là đối tượng tạo sẵn trong Java Script engine, bạn sử dụng các thuộc tính và phương thức của XMLHttpRequest thông qua những thể hiện, như cách chúng ta vẫn làm với các đối tượng Array, Date...

Để FireFox hoặc Netscape làm điều mà IE đã làm với Data Island trong ví dụ đầu tiên, bạn có thể sử dụng đối tượng XMLHttpRequest như trong script sau :
<script type="text/javascript">
function parseXML(){
  var req=new XMLHttpRequest();
   req.open("GET", "present.xml", false);
req.send(null);
  var oNode = req.responseXML.getElementsByTagName ('present')[0];
  var str='';
    if(oNode.hasChildNodes()){
        for(var i=0;i<oNode.childNodes.length;i++){
          if(oNode.childNodes[i].nodeValue==null){
            str += '<b>'+oNode.childNodes[i].nodeName+'</b>';
          }
            var k= oNode.childNodes[i];
            if(k.hasChildNodes()){
                for(var j=0;j<k.childNodes.length;j++){
                    str +=" : <font color=blue>"+k.childNodes[j].nodeValue+"</font><br>";
                }
            }
        }
    }
return str;
}

var p=parseXML();
    document.write(p);

</script>


Và đây là kết quả trên Netscape 9.0 :


Iframe caching, mà chúng ta nói đến ở phần đầu tiên của topic này, không phải là AJAX. Đó chỉ là một sáng tạo của các nhà phát triển web trong thời kỳ tiền AJAX, nhằm đáp ứng một đòi hỏi chính đáng của người sử dụng. Đòi hỏi ấy là :

Hãy cho tôi xem nội dung khác, nhưng đừng bắt tôi phải tải lại cả trang !

XMLHttpRequest đã cung cấp một cách thức xử lý linh hoạt và dễ kiểm soát hơn cho cùng một công việc.

Trong số các phương thức của XMLHttpRequest, open là đáng lưu ý nhất. Nó quyết định cách thức mà bạn sẽ phải viết script trong hàm xử lý. Trên interface, bạn có thể thấy phương thức này có 2 tham số bắt buộc là Method và chuỗi URI. Tham số thứ 3, Async, nhận vào 1 giá trị boolean,

Mặc định, async có giá trị true nếu bạn không cung cấp. Nếu async là false, script sẽ chờ cho tiến trình load kết thúc rồi mới thực thi phần kịch bản tiếp theo. Nếu async là true, script sẽ nhảy qua câu lệnh kế tiếp mà không chờ tiến trình load kết thúc, và bạn sẽ cần 1 hàm theo dõi tiến trình load để quyết định khi nào nên bắt đầu xử lý kết quả trả về.

URI là file đích sẽ tiếp nhận request. FireFox chỉ chấp nhận 1 đường dẫn tương đối. Nếu bạn cố gắng gửi yêu cầu đến 1 URL đầy đủ, ngay cả khi cùng 1 domain, bạn sẽ nhận 1 lỗi.

Method có thể giữ 1 trong các giá trị GET, POST, HEAD, PUT, DELETE hoặc OPTIONS . Tên của method được quy định viết kiểu chữ in, nằm trong ngoặc kép. GET, POST ở đây có vai trò như khi làm thuộc tính method của các form. Script trên đã tải xuống file present.xml bằng phương thức GET.

Nếu sử dụng giao thức POST, vì POST nén dữ liệu thành những packets khi giao dịch, nên ít nhất chúng ta phải có 1 dòng setRequestHeader để thiết lập Content-Type cho gói tin. Ví dụ dưới đây hàm doLogin nhận vào 2 tham số username, password và POST chúng lên 1 file login.php :

function doLogin(username, password){
  var req=request();
  var $packet="user="+username+"& pwd="+password;
    if(typeof(req)!=undefined){
      req.onreadystatechange =function(){
        if(req.readyState==4&&req.status==200){
            // do something when data is received...
        }
      }
req.open("POST", 'login.php', true);          
req.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");
req.send($packet);
    }
}

Ở đây, hàm request có nhiệm vụ định nghĩa 1 thể hiện cho XMLHttpRequest trong Netscape hoặc XMLHTTP trong IE :

function request(){
   var req=null;
      if(XMLHttpRequest){
         req=new XMLHttpRequest();
     }
     else if(window.ActiveX){
        req=new ActiveXObject("Msxml2.XMLHTTP");
            if(!req){
               req=new ActiveXObject("Microsoft.XMLHTTP");
           }
     }
  return req;
}


Trong file login.php sẽ có đoạn :
...
$Username=$_POST['user'];
$Password=$_POST['pwd '];
...

Các giá trị HEAD, PUT, DELETE ít thấy sử dụng cho Method, chúng ta sẽ dành thời gian để nói về SOAP. Như trên đã giới thiệu, SOAP là 1 giao thức mới, dựa trên POST. Bạn có thể dùng AJAX để gửi và nhận dữ liệu thông qua giao thức SOAP, nhưng cần thêm một số thiết lập khác trong code Java Script của bạn. Phần sau chúng ta sẽ tạo ra một Web Service đơn giản trên nền Appache/PHP để có thể làm một ví dụ về AJAX - SOAP.