目次
- 1. 同期および非同期なリクエスト
- 1.1. 例:同期リクエスト
- 1.2. 例: HTTP を用いない同期リクエスト
- 1.3. Example: Asynchronous request
- 2. Handling binary data
- 2.1. Receiving binary data
- 2.2. Sending binary data
- 3. Monitoring progress
- 4. Cross-site XMLHttpRequest
- 5. Bypassing the cache
- 6. Security
- 7. Downloading JSON and JavaScript from extensions
- 8. Using XMLHttpRequest from XPCOM components
- 9. See also
XMLHttpRequest は HTTP リクエストを送信することをとても簡単に実現します。単にオブジェクトのインスタンスを作成し、URL を開き、リクエストを送信するだけです。結果の HTTP ステータスとコンテンツは、一連の通信が完了したときにリクエストオブジェクトを用いて参照できます。
同期および非同期なリクエスト
XMLHttpRequest は同期および非同期なコミュニケーションの双方をサポートします。
XMLHttpRequest は使うべきではありません。 なぜなら、ネットワークの本質的に非同期な特性のために、同期リクエストを用いたときにメモリリークやイベントがこぼれ落ちる様々な方法が存在するからです。Firefox 3 より前のバージョンの Firefox では、同期 XMLHttpRequest リクエストはユーザインタフェースをブロックします。ユーザがフリーズしたリクエストを中止できるように、Firefox 3 ではそのようなことを行いません。
例:同期リクエスト
この例は単純な同期リクエストを作成する方法を示しています。
var req = new XMLHttpRequest();
req.open('GET', 'http://www.mozilla.org/', false);
req.send(null);
if(req.status == 200)
dump(req.responseText);
1 行目では XMLHttpRequest オブジェクトをインスタンス化しています。それから 2 行目で新しいリクエストをオープンし、Mozilla.org のホームページを取得するための GET リクエスト、かつその動作は非同期であるべきではないことを指定しています。
3 行目ではリクエストを送っています。null パラメータは GET リクエストのための body の内容は必要とされないことを示しています。
4 行目では一連の通信の完了後にステータスコードをチェックしています。もし結果が 200 -- HTTP での "OK" を意味する結果 -- であるなら、文書のテキストの内容がコンソールに出力されるでしょう。
例: HTTP を用いない同期リクエスト
その名前に関わらず、XMLHttpRequest は HTTP を用いないリクエストで用いることもできます。以下の例はローカルファイルシステムからファイルを取得する方法を示しています。
var req = new XMLHttpRequest();
req.open('GET', 'file:///home/user/file.json', false);
req.send(null);
if(req.status == 0)
dump(req.responseText);
ここで注意するべき重要なことは成功かどうかの結果のステータスが 200 ではなく 0 と比較されていることです。その理由は file および ftp スキーマは HTTP 結果コードを利用しないからです。
Example: Asynchronous request
If you use XMLHttpRequest from an extension, you should use it asynchronously. In this case, you receive a callback when the data has been received, which lets the browser continue to work as normal while your request is being handled.
var req = new XMLHttpRequest();
req.open('GET', 'http://www.mozilla.org/', true);
req.onreadystatechange = function (aEvt) {
if (req.readyState == 4) {
if(req.status == 200)
dump(req.responseText);
else
dump("Error loading page\n");
}
};
req.send(null);
Line 2 specifies true for its third parameter to indicate that the request should be handled asynchronously.
Line 3 creates an event handler function object and assigns it to the request's onreadystatechange attribute. This handler looks at the request's readyState to see if the transaction is complete in line 4, and if it is, and the HTTP status is 200, dumps the received content. If an error occurred, an error message is displayed.
Line 11 actually initiates the request. The callback routine is called whenever the state of the request changes.
Handling binary data
Although XMLHttpRequest is most commonly used to send and receive textual data, it can be used to send and receive binary content.
Receiving binary data
The load_binary_resource() function shown below loads binary data from the specified URL, returning it to the caller.
function load_binary_resource(url) {
var req = new XMLHttpRequest();
req.open('GET', url, false);
//XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
req.overrideMimeType('text/plain; charset=x-user-defined');
req.send(null);
if (req.status != 200) return '';
return req.responseText;
}
The magic happens in line 5, which overrides the MIME type, forcing Firefox to treat it as plain text, using a user-defined character set. This tells Firefox not to parse it, and to let the bytes pass through unprocessed.
var filestream = load_binary_resource(url); var abyte = filestream.charCodeAt(x) & 0xff; // throw away high-order byte (f7)
The example above fetches the byte at offset x within the loaded binary data. The valid range for x is from 0 to filestream.length-1.
See downloading binary streams with XMLHttpRequest for a detailed explanation. See also downloading files.
Sending binary data
This example transmits binary content asynchronously, using the POST method.
var req = new XMLHttpRequest();
req.open("POST", url, true);
// set headers and mime-type appropriately
req.setRequestHeader("Content-Length", 741);
req.sendAsBinary(aBody);
Line 4 sets the Content-Length header to 741, indicating that the data is 741 bytes long. Obviously you need to change this value based on the actual size of the data being sent.
Line 5 uses the sendAsBinary() method to initiate the request.
You can also send binary content by passing an instance of the nsIFileInputStream
to send(). In that case, you don't have to set the Content-Length header yourself, as the information is fetched from the stream automatically:
// Make a stream from a file.
var stream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
stream.init(file, 0x04 | 0x08, 0644, 0x04); // file is an nsIFile instance
// Try to determine the MIME type of the file
var mimeType = "text/plain";
try {
var mimeService = Components.classes["@mozilla.org/mime;1"]
.getService(Components.interfaces.nsIMIMEService);
mimeType = mimeService.getTypeFromFile(file); // file is an nsIFile instance
}
catch(e) { /* eat it; just use text/plain */ }
// Send
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Components.interfaces.nsIXMLHttpRequest);
req.open('PUT', url, false); /* synchronous! */
req.setRequestHeader('Content-Type', mimeType);
req.send(stream);
Monitoring progress
XMLHttpRequest provides the ability to listen to various events that can occur while the request is being processed. This includes periodic progress notifications, error notifications, and so forth.
In Firefox 3.5 and later
var req = new XMLHttpRequest();
req.addEventListener("progress", updateProgress, false);
req.addEventListener("load", transferComplete, false);
req.addEventListener("error", transferFailed, false);
req.addEventListener("abort", transferCanceled, false);
req.open();
...
// progress on transfers from the server to the client (downloads)
function updateProgress(evt) {
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
...
} else {
// Unable to compute progress information since the total size is unknown
}
}
function transferComplete(evt) {
alert("The transfer is complete.");
}
function transferFailed(evt) {
alert("An error occurred while transferring the file.");
}
function transferCanceled(evt) {
alert("The transfer has been canceled by the user.");
}
Lines 3-6 add event listeners for the various events that are sent while performing a data transfer using XMLHttpRequest. See nsIDOMProgressEvent
and nsIXMLHttpRequestEventTarget
for details on these events.
open() on the request. Otherwise the progress events will not fire.The progress event handler, specified by the updateProgress() function in this example, receives the total number of bytes to transfer as well as the number of bytes transferred so far in the event's total and loaded fields. However, if the lengthComputable field is false, the total length is not known and will be zero.
Progress events exist for both download and upload transfers. The download events are fired on the XMLHttpRequest object itself, as shown in the above sample. The upload events are fired on the XMLHttpRequest.upload object, as shown below:
var req = new XMLHttpRequest();
req.upload.addEventListener("progress", updateProgress, false);
req.upload.addEventListener("load", transferComplete, false);
req.upload.addEventListener("error", transferFailed, false);
req.upload.addEventListener("abort", transferCanceled, false);
req.open();
In Firefox 3 and earlier
If, for example, you wish to provide progress information to the user while the document is being received, you can use code like this:
function onProgress(e) {
var percentComplete = (e.position / e.totalSize)*100;
// ...
}
function onError(e) {
alert("Error " + e.target.status + " occurred while receiving the document.");
}
function onLoad(e) {
// ...
}
// ...
var req = new XMLHttpRequest();
req.onprogress = onProgress; // or req.addEventListener("progress", onProgress, false);
req.open("GET", url, true);
req.onload = onLoad; // or req.addEventListener("load", onLoad, false);
req.onerror = onError; // or req.addEventListener("error", onError, false);
req.send(null);
The onprogress event's attributes, position and totalSize, indicate the current number of bytes received and the total number of bytes expected, respectively.
All of these events have their target attribute set to the XMLHttpRequest they correspond to.
target, currentTarget, and this fields of the event object are set to reference the correct objects when calling event handlers for XML documents represented by XMLDocument. See
bug 198595
for details.Cross-site XMLHttpRequest
Firefox 3.5 supports cross-site requests by implementing the web applications working group's Access Control for Cross-Site Requests standard. As long as the server is configured to allow requests from your web application's origin, XMLHttpRequest will work. Otherwise, an INVALID_ACCESS_ERR exception is thrown.
Bypassing the cache
Normally, XMLHttpRequest tries to retrieve content from the cache, if it's available. To bypass this, do the following:
var req = new XMLHttpRequest();
req.open('GET', url, false);
req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
req.send(null);
channel attribute is Gecko-specific.An alternate, cross-browser compatible approach is to append a timestamp to the URL, being sure to include a "?" or "&" as appropriate. For example:
http://foo.com/bar.html
becomes
http://foo.com/bar.html?12345
and
http://foo.com/bar.html?foobar=baz
becomes
http://foo.com/bar.html?foobar=baz&12345
Since the local cache is indexed by URL, this causes every request to be unique, thereby bypassing the cache.
You can automatically adjust URLs using the following code:
var req = new XMLHttpRequest();
req.open("GET", url += (url.match(/\?/) == null ? "?" : "&") + (new Date()).getTime(), false);
req.send(null);
Security
Downloading JSON and JavaScript from extensions
For security reasons, extensions should never use eval() to parse JSON or JavaScript code downloaded from the web. See Downloading JSON and JavaScript in extensions for details.
Using XMLHttpRequest from XPCOM components
Instantiating XMLHttpRequest from an XPCOM component works a little differently; it can't be instantiated using the XMLHttpRequest() constructor from a JavaScript XPCOM component. The constructor is not defined inside components and the code results in an error. You'll need to create and use it using a different syntax.
Instead of this:
var req = new XMLHttpRequest();
req.onprogress = onProgress;
req.onload = onLoad;
req.onerror = onError;
req.open("GET", url, true);
req.send(null);
Do this:
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Components.interfaces.nsIXMLHttpRequest);
req.onprogress = onProgress;
req.onload = onLoad;
req.onerror = onError;
req.open("GET", url, true);
req.send(null);
For C++ code you would need to QueryInterface the component to an nsIEventTarget in order to add event listeners, but chances are in C++ using a channel directly would be better.
See also
- MDC AJAX introduction
- HTTP access control
- How to check the security state of an XMLHTTPRequest over SSL
- XMLHttpRequest - REST and the Rich User Experience
- Microsoft documentation
- Apple developers' reference
- "Using the XMLHttpRequest Object" (jibbering.com)
- The XMLHttpRequest Object: W3C Working Draft
- Web Progress Events specification

