数据串行化:
要发送一系列的数据到服务器上,第一步要整理他的格式,使服务器易于读取,这一过程称为“串行化”。串行化有2种不同的情况,但都能满足多种不同的传输需求。
1.传输一个常规的js对象,其中可能包含键/值(值可能是字符串或数字)
2.从一系列的表单的输入栏中提交。这种情况和第一种情况的不同之处在于提交的元素要按顺序排列,而第一个情况可以任意排列.
// A simple object holding key/value pairs { name: "John", last: "Resig", city: "Cambridge", zip: 02140 } // Serialized form name=John&last=Resig&city=Cambridge&zip=02140 // Another set of data, with multiple values [ { name: "name", value: "John" }, { name: "last", value: "Resig" }, { name: "lang", value: "JavaScript" }, { name: "lang", value: "Perl" }, { name: "lang", value: "Java" } ] // And the serialized form of that data name=John&last=Resig&lang=JavaScript&lang=Perl&lang=Java // Finally, lets find some input elements (using the id() method that // we made in the DOM chapter) [ id( "name" ), id( "last" ), id( "username" ), id( "password" ) ] // And serialize them into a data string name=John&last=Resig&username=jeresig&password=test
下面我们建立一个将上面的数据结构串行化的标准方法。下面的函数可以对大多数表单元素进行串行化,不过多选按钮除外。
// Serialize a set of data. It can take two different types of objects: // - An array of input elements. // - A hash of key/value pairs // The function returns a serialized string function serialize(a) { // The set of serialize results var s = []; // If an array was passed in, assume that it is an array // of form elements if ( a.constructor == Array ) { // Serialize the form elements for ( var i = 0; i < a.length; i++ ) s.push( a[i].name + "=" + encodeURIComponent( a[i].value ) ); // Otherwise, assume that it's an object of key/value pairs } else { // Serialize the key/values for ( var j in a ) s.push( j + "=" + encodeURIComponent( a[j] ) ); } // Return the resulting serialization return s.join("&"); }
encodeURIComponent转义字符串组件
建立一个能够完整处理ajax请求的函数:
1.处理错误
成功响应代码:状态码在200到300之间的属于成功的请求
未修改响应码:Not modified 304
本地存储的文件:如果你在本机上之间执行ajax应用程序,而不通过web服务器,就算请求成功了,也不会得到任何返回的状态码。者意味着,在执行本地文件请求且得不到状态码时,你应该把这种情况算做成功的响应。
safari与未修改状态
如果文档自上次请求(或者通过浏览器明确发送一个if modified since首部,指定上次修改过的时间给服务器)未曾修改过,safari返回的状态码会是undefined。这是一个比较怪异的情形,也让人难以调试。
// Check to see if an XMLHttpRequest object has a 'Success' state, or not. // The function takes one argument, the XMLHttpRequest object function httpSuccess(r) { try { // If no server status is provided, and we're actually // requesting a local file, then it was successful return !r.status && location.protocol == "file:" || // Any status in the 200 range is good ( r.status >= 200 && r.status < 300 ) || // Successful if the document has not been modified r.status == 304 || // Safari returns an empty status if the file has not been modified navigator.userAgent.indexOf("Safari") >= 0 && typeof r.status == "undefined"; } catch(e){} // If checking the status failed, then assume that the request failed too return false; }
检查http响应的成功状态是非常重要的一部,不做检查可能导致许多难以预料的结果,比如服务器返回的其实是html的错误页面,而非xml温度。
我们把这个函数集成在完整的ajax方案中。
2.检查超时。
在XMLHttpRequest的默认实现中缺乏的另一个有用的功能是如何判断请求超时。
我们可以利用setTimeout()来完成这个功能:
// Create the request object var xml = new XMLHttpRequest(); // Open the asynchronous POST request xml.open("GET", "/some/url.cgi", true); // We're going to wait for a request for 5 seconds, before giving up var timeoutLength = 5000; // Keep track of when the request has been succesfully completed var requestDone = false; // Initalize a callback which will fire 5 seconds from now, cancelling // the request (if it has not already occurred). setTimeout(function(){ requestDone = true; }, timeoutLength); // Watch for when the state of the document gets updated xml.onreadystatechange = function(){ // Wait until the data is fully loaded, // and make sure that the request hasn't already timed out if ( xml.readyState == 4 && !requestDone ) { // xml.responseXML contains the XML Document (if one was returned) // xml.responseText contains the response text (if no XML document was provided) // Clean up after ourselves, to avoid memory leaks xml = null; } }; // Establish the connection to the server xml.send();
3处理响应的数据
responseXML,如果服务器返回的是xml温度,必须明确指定其内容首部(content header)是‘Content-type:text/xml",这一点才起作用。
// A function for extracting data from an HTTP reponse // It takes two arguments, the XMLHttpRequest object and // An optional argument – the type of data that you're expecting from the server // Correct values include: xml, script, text, or html – the default is "", which // determines what the data type is based upon the content-type header function httpData(r, type) { // Get the content-type header var ct = r.getResponseHeader("content-type"); // If no default type was provided, determine if some // form of XML was returned from the server var data = !type && ct && ct.indexOf("xml") >= 0; // Get the XML Document object if XML was returned from // the server, otherwise return the text contents returned by the server data = type == "xml" || data ? r.responseXML : r.responseText; // If the specified type is "script", execute the returned text // response as if it was JavaScript if ( type == "script" ) eval.call( window, data ); // Return the response data (either an XML Document or a text string) return data; }
完整的ajax程序包
// A generic function for performming AJAX requests // It takes one argument, which is an object that contains a set of options // All of which are outline in the comments, below function ajax( options ) { // Load the options object with defaults, if no // values were provided by the user options = { // The type of HTTP Request type: options.type || "POST", // The URL the request will be made to url: options.url || "", // How long to wait before considering the request to be a timeout timeout: options.timeout || 5000, // Functions to call when the request fails, succeeds, // or completes (either fail or succeed) onComplete: options.onComplete || function(){}, onError: options.onError || function(){}, onSuccess: options.onSuccess || function(){}, // The data type that'll be returned from the server // the default is simply to determine what data was returned from the // and act accordingly. data: options.data || "" }; // Create the request object var xml = new XMLHttpRequest(); // Open the asynchronous POST request xml.open("GET", "/some/url.cgi", true); // We're going to wait for a request for 5 seconds, before giving up var timeoutLength = 5000; // Keep track of when the request has been succesfully completed var requestDone = false; // Initalize a callback which will fire 5 seconds from now, cancelling // the request (if it has not already occurred). setTimeout(function(){ requestDone = true; }, timeoutLength); // Watch for when the state of the document gets updated xml.onreadystatechange = function(){ // Wait until the data is fully loaded, // and make sure that the request hasn't already timed out if ( xml.readyState == 4 && !requestDone ) { // Check to see if the request was successful if ( httpSuccess( xml ) ) { // Execute the success callback with the data returned from the server options.onSuccess( httpData( xml, options.type ) );//我们把返回的data传给了函数的参数 // Otherwise, an error occurred, so execute the error callback } else { options.onError(); } // Call the completion callback options.onComplete(); // Clean up after ourselves, to avoid memory leaks xml = null; } }; // Establish the connection to the server xml.send(); // Determine the success of the HTTP response function httpSuccess(r) { try { // If no server status is provided, and we're actually // requesting a local file, then it was successful return !r.status && location.protocol == "file:" || // Any status in the 200 range is good ( r.status >= 200 && r.status < 300 ) || // Successful if the document has not been modified r.status == 304 || // Safari returns an empty status if the file has not been modified navigator.userAgent.indexOf("Safari") >= 0 && typeof r.status == "undefined"; } catch(e){} // If checking the status failed, then assume that the request failed too return false; } // Extract the correct data from the HTTP response function httpData(r,type) { // Get the content-type header var ct = r.getResponseHeader("content-type"); // If no default type was provided, determine if some // form of XML was returned from the server var data = !type && ct && ct.indexOf("xml") >= 0; // Get the XML Document object if XML was returned from // the server, otherwise return the text contents returned by the server data = type == "xml" || data ? r.responseXML : r.responseText; // If the specified type is "script", execute the returned text // response as if it was JavaScript if ( type == "script" ) eval.call( window, data ); // Return the response data (either an XML Document or a text string) return data; } }