XMLHttpRequest

XMLHttpRequest (XHR) 是一個 JavaScript類，包含將HTTP請求從網路瀏覽器異步傳輸到網路伺服器的方法。這些方法允許基於瀏覽器的應用程式進行細粒度的伺服器調用並把存儲結果存儲在XMLHttpRequest的`responseText`特性中。XMLHttpRequest類是Ajax編程的一個組件。在Ajax出現之前，需要將網頁表單完全發送到伺服器，然後刷新整個瀏覽器頁面。

## 歷史

「XMLHttpRequest」類背後的概念是由Microsoft Outlook 的開發人員於2000年提出的——可在Windows 2000作業系統上使用。然後這個概念在Internet Explorer 5 (2001) 瀏覽器的 解釋器 中實現。但是，原始的語法沒有使用`XMLHttpRequest`標識符。相反，開發人員使用了標識符`ActiveXObject("Msxml2.XMLHTTP")`和`ActiveXObject("Microsoft.XMLHTTP")`。到Internet Explorer 7 (2006)，所有的瀏覽器都支持`XMLHttpRequest`標識符。

`XMLHttpRequest`標識符現在是所有現代瀏覽器的事實標準，包括Mozilla的Gecko layout engine (2002), Konqueror (2002), Safari 1.2 (2004), Opera 8.0 (2005),, and iCab (2005).

隨著跨瀏覽器 JavaScript 庫（例如 jQuery）的出現，開發人員可以間接調用 XMLHttpRequest 功能。

## 標準

全球資訊網聯盟 (W3C)於2006年4月5日發布了XMLHttpRequest對象的工作草案規範。2008年2月25日，W3C發布了Working Draft Level 2規範。Level 2規範添加了監視事件進度、允許跨站點請求和處理字節流的方法。2011年底，Level 2規範被吸收到原始規範中。

2012年底，WHATWG接管開發並使用Web IDL維護動態文檔。

## XMLHttpRequest用法

### 構造器

生成發給Web伺服器的異步請求首先要實例化分配記憶體）「XMLHttpRequest」對象。分配的記憶體（的地址）被賦值給變量。JavaScript中實例化新對象的編程 語句 為`new`。`new`語句後跟對象的構造函數。面向對象語言的開發人員的習慣是使用與類名相同的名稱來調用構造函數本例中，類名是XMLHttpRequest。實例化一個新的XMLHttpRequest並賦值給變量`request`:

`var request = new XMLHttpRequest();`

### open方法

open方法準備XMLHttpRequest。它最多可接受五個參數，但只有前兩個是必須的。

`var request = new XMLHttpRequest();`

`request.open( RequestMethod, SubmitURL, AsynchronousBoolean, UserName, Password );`

- RequestMethod: 對於典型的數據量，HTTP請求方法可以是`GET`。在其他可用的請求方法中，`POST`將處理大量數據。收到返回字串後，將DELETE請求方法發送到`.open()`以釋放 XMLHttpRequest 記憶體。如果發送`DELETE`，則 SubmitURL 參數可為`null`。
- `request.open( "DELETE", null );`
- SubmitURL: SubmitURL是包含執行文件名和提交到Web伺服器的任何參數的URL。如果URL包含主機名，則它一定是發送HTML文檔的Web伺服器。Ajax支持同源策略。
- AsynchronousBoolean: 如果提供該參數，則應將其設置為true。如果設置為false，則瀏覽器將等待，直到收到返回字串。不鼓勵程式設計師將AsynchronousBoolean設置為false，瀏覽器可能會遇到異常錯誤。
- UserName:如果提供，它將有助於驗證使用者身份。
- Password: 如果提供，它將有助於驗證使用者身份。

### setRequestHeader方法

如果調用`POST`請求方法，則需要額外發送媒體類型`Content-Type: application/x-www-form-urlencoded`。`setRequestHeader`方法允許程序將此或其他HTTP標頭髮送到Web伺服器。其用法是`setRequestHeader( HeaderField, HeaderValue )`。啟用`POST`請求方法:

`request.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );`

### send方法

如果調用`POST`請求方法，則Web伺服器期望從標準輸入流讀取表單數據。要將表單數據發送到Web伺服器，請執行`request.send( FormData )`，其中FormData是文本字符串。如果調用`GET`請求方法，則Web伺服器只需要默認標頭。要發送默認標頭，請執行`request.send( null )`.

### onreadystatechange事件監聽器

`onreadystatechange`是一個回調方法，在整個Ajax生命周期中定期執行。要設置名為 `ReadyStateMethod()`的回調方法，語法為`request.onreadystatechange = ReadyStateMethod`。為了方便起見，該語法允許定義匿名方法。定義匿名回調方法:

```javascript
var request = new XMLHttpRequest();

request.onreadystatechange = function()
{
// code omitted
}
```

XMLHttpRequest生命周期經歷幾個階段 - 從0到4。階段0是調用`open()`方法之前，階段4是文本字符串到達時。為了監視生命周期，XMLHttpRequest具有可用的`readyState`屬性。第1-3階段不明確，不同瀏覽器的解釋也不同。儘管如此，一種解釋是：

- 階段 0：未初始化
- 第一階段：加載中
- 第二階段：加載完成
- 第三階段：交互
- 第四階段：已完成

當`readyState`達到4時，文本字符串已經到達並被設置在`responseText`屬性中。

```javascript
var request = new XMLHttpRequest();

request.onreadystatechange = function()
{
    if ( request.readyState == 4 )
    {
        // request.responseText is set
    }
}
```

## 例子

下例首先創建Javascript函數:

- `cd /var/www/html`
- 編輯文件`ajax_submit.js`:

```javascript
function ajax_submit( element_id, submit_url )
{
    var request = new XMLHttpRequest();
    var completed_state = 4;

    request.onreadystatechange = function()
    {
        if ( request.readyState == completed_state )
        {
            document.
                getElementById( element_id ).
                innerHTML =
                    request.responseText;
            request.open( "DELETE", null );
        }
    }

    request.open( "GET", submit_url );
    request.send( null );
}
```

- 同一個目錄下編輯文件`ajax.phtml`:

```PHP
<?php
    echo '<h1>Hello World!</h1>';
?>
```

- 同一個目錄下編輯文件`ajax.html`:

```html
<html>
<head>
    <title>Hello World</title>
    <script type=text/javascript src=ajax_submit.js></script>
</head>
<body>
    <div id=ajax_title></div>
    <button onclick="ajax_submit( 'ajax_title', 'ajax.phtml' )">
        Submit
    </button>
</body>
```

- 在瀏覽器中打開`http://localhost/ajax.html`
- 點擊`Submit`

## CGI例子

通用網關接口 (CGI) 進程允許瀏覽器請求Web伺服器執行已編譯的電腦程式。

CGI XMLHttpRequest的伺服器組件是位於伺服器上的可執行文件。作業系統將打開該文件並讀取其機器指令。XMLHttpRequest協議要求可執行文件輸出文本字符串。

編譯後的程序有兩個文件：原始碼和相應的可執行文件。

- `cd /usr/lib/cgi-bin`
- 編輯文件`ajax.c`:

```c
#include <stdio.h>

void main( void )
{
    /* CGI requires the first line to output: */
    printf( "Content-type: text/html\n" );

    /* CGI requires the second line to output: */
    printf( "\n" );

    printf( "<h1>Hello World!</h1>\n" );
}
```

`cc ajax.c -o ajax`

或

`sudo cc ajax.c -o ajax`

CGI瀏覽器組件與PHP瀏覽器組件相同，只是`submit_url`略有變化。告訴Web伺服器執行可執行文件的語法是`/cgi-bin/`後跟文件名。為了安全起見，可執行文件必須駐留在chroot 監獄中。在本例中，監獄是目錄`/usr/lib/cgi-bin/`.

- `cd /var/www/html`
- 編輯文件`ajax.html`:

```html
<html>
<head>
    <title>Hello World</title>
    <script type=text/javascript src=ajax_submit.js></script>
</head>
<body>
    <div id=ajax_title></div>
    <button onclick="ajax_submit( 'ajax_title', '/cgi-bin/ajax' )">
        Submit
    </button>
</body>
```

- 用瀏覽器打開`http://localhost/ajax.html`
- 點擊`Submit`

## 外部連結

- XMLHttpRequest Living Standard by the WHATWG
- XMLHttpRequest Level 1 draft by the W3C