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