摘要:能異步地發送任意數據的技術稱為,表示異步的和。若你使用,使用發送表單還會影響同源策略,并導致內容被發送到一個無法訪問的中。但要手動發送二進制數據的話,還有很多額外工作要做。用來發送二進制是很直接的,使用方法就好了。
系列文章說明
原文
在[發送表單數據]()一文中,HTML表單可以聲明式地發送一個HTTP請求。但表單也可以用JavaScript來準備一個HTTP請求。本文將探索如何做到這點。
表單,不只是表單隨著開放式Web應用的出現,現在提供HTML表單、而不是文字表單供用戶填寫的做法,已經越發普遍了。而越來越多的開發者也獲得了數據傳輸的控制權。
獲得對數據傳輸的控制權標準的HTML表單提交操作會加載數據發送到的URL,這就意味著瀏覽器的窗口中會進行整個頁面的重新加載。而如果避免了頁面的重新加載,就會避免頁面的閃爍和網絡延遲,進而提供更順暢的用戶體驗。
在許多現代的UI設計中,HTML表單只是用來收集用戶的輸入。當用戶要發送數據時,Web應用會進行控制,并在后臺異步地發送數據,只更新UI中需要更改的部分。
能異步地發送任意數據的技術稱為AJAX,表示“異步的JavaScript和XML”。
和傳統表單處理的不同AJAX使用了XMLHttpRequest(XHR)DOM對象,它可以建立HTTP請求、發送請求并處理結果。
注意: 老的AJAX技術可能不是用XMLHttpRequest。比如JSONP和eval()函數結合起來使用。雖然該方法可行,但不推薦使用它,因為其存在嚴重的安全問題。所以除非為了兼容那些特別老舊、不支持XMLHttpRequest或JSON的瀏覽器,還是避免使用該技術。
由于歷史原因,XMLHttpRequest本是設計用來獲取和發送交換格式為XML的數據的。但現在JSON取代了XML,有著更普遍的使用。
不過XML和JSON都不符合作為表單數據請求的編碼。表單數據(application/x-www-form-urlencoded)是用于構造鍵值對的URL編碼列表的,若是要傳輸二進制數據,HTTP請求會被重塑為 multipart/form-data。
若你能掌控前端(運行在瀏覽器上的代碼)和后端(運行在服務器上的代碼),你就能發送JSON或XML、并隨心所欲地處理它們。
但如果你使用的是第三方服務,這就沒那么容易了,因為某些服務只接受表單數據。當然也有使用表單數據處理起來更方便的情況,比如數據是鍵值對或二進制數據時,用現成的后端工具就能處理它們、不需要額外的代碼。
那么,具體該如何發送數據呢?
發送表單數據目前有三種方式來發送表單數據,既有老舊的技術、也有新特性FormData對象,接下來就來深入了解下它們。
在隱藏的iframe中構建DOM發送表單數據最古老的方法,是用DOM API建立一個表單,然后發送數據到一個隱藏的。為了訪問你提交內容的處理結果,應檢索下的內容。
警告:避免使用該技術。在使用第三方服務時,該技術有安全風險,因為它會導致你面臨腳本注入攻擊。若你使用HTTPS,使用
發送表單還會影響同源策略,并導致內容被發送到一個無法訪問的 中。當然如果你要兼容很老舊的瀏覽器,這項技術可能就是你唯一的選擇了。
下面是一個例子:
// 創建一個iFrame來發送我們的數據 var iframe = document.createElement("iframe"); iframe.name = "myTarget"; // 將iFrame添加到文檔流中 window.addEventListener("load", function () { iframe.style.display = "none"; document.body.appendChild(iframe); }); // 用來發送數據的函數 // 需要一個參數,是一個由鍵值對組成的對象 function sendData(data) { var name, form = document.createElement("form"), node = document.createElement("input"); // 定義響應加載時的行為 iframe.addEventListener("load", function () { alert("Yeah! Data sent."); }); form.action = "http://www.cs.tut.fi/cgi-bin/run/~jkorpela/echo.cgi"; form.target = iframe.name; for(name in data) { node.name = name; node.value = data[name].toString(); form.appendChild(node.cloneNode()); } // 要發送數據,表單得添加到文檔流中 form.style.display = "none"; document.body.appendChild(form); form.submit(); // 表單一發送就移除它 document.body.removeChild(form); }
結果如下:
效果
XMLHttpRequest是發送HTTP請求最安全和可靠的方式。要想用XMLHttpRequest發送表單數據,得先用URL編碼要發送的數據,并遵循表單數據請求的規范。
注意: 若想了解更多關于XMLHttpRequest,這幾篇文章可能對你有用:An introductory article to AJAX,以及一個關于使用XMLHttpRequest的高級教程。
來重構下我們先前的例子:
如你所見,HTML部分并未真的有所改變,但JavaScript部分就完全不同了:
function sendData(data) { var XHR = new XMLHttpRequest(); var urlEncodedData = ""; var urlEncodedDataPairs = []; var name; // 將data對象轉為一個URL編碼的鍵值對數組 for(name in data) { urlEncodedDataPairs.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name])); } // 將鍵值對組合成一個字符串,并把所有經過URL編碼的空格替換為"+"號;以符合瀏覽器表單提交的行為 // the "+" character; matches the behaviour of browser form submissions. urlEncodedData = urlEncodedDataPairs.join("&").replace(/%20/g, "+"); // 定義成功的數據提交后會發生什么 XHR.addEventListener("load", function(event) { alert("Yeah! Data sent and response loaded."); }); // 定義失敗的情況會發生什么 XHR.addEventListener("error", function(event) { alert("Oups! Something goes wrong."); }); // 配置請求 XHR.open("POST", "https://example.com/cors.php"); // 添加表單數據POST請求所需的HTTP請求頭 XHR.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // 最后,發送數據 XHR.send(urlEncodedData); }
結果如下:
效果
使用XMLHttpRequest和FormData對象注意: 若你想使用此處的XMLHttpRequest方法發送數據到第三方網站,也會受到同源策略的限制。要實現跨域請求,還需要CORS和HTTP訪問控制。
手動構建HTTP請求是挺麻煩的,幸運的是,最近的一項XMLHttpRequest標準提供了一個處理表單請求的簡便方法--FormData對象。
FormData對象可被用來建立要傳輸的表單數據,或者從表單元素中獲取數據來管理如何發送。要注意FormData對象是“只寫”的,這意味著你可以改變它們、但不能訪問它們的內容。
使用FormData對象的方法會在[使用FormData對象]()一文中詳細介紹,下面有兩個例子:
使用獨立的FormData對象現在你該很熟悉這個HTML的樣子了。
function sendData(data) { var XHR = new XMLHttpRequest(); var FD = new FormData(); // 將數據添加到FormData對象中 for(name in data) { FD.append(name, data[name]); } // 定義成功的數據提交后會發生什么 XHR.addEventListener("load", function(event) { alert("Yeah! Data sent and response loaded."); }); // 定義失敗的情況會發生什么 XHR.addEventListener("error", function(event) { alert("Oups! Something went wrong."); }); // 配置請求 XHR.open("POST", "https://example.com/cors.php"); // 發送FormData對象,HTTP頭會自動設置 XHR.send(FD); }
結果如下:
效果
你也可以將FormData對象綁定到一個表單元素上,這樣做會創建一個FormData對象來表示表單中的數據。
HTML是典型的表單:
但JavaScript會接管表單的提交操作:
window.addEventListener("load", function () { function sendData() { var XHR = new XMLHttpRequest(); // 綁定FormData對象和表單元素 var FD = new FormData(form); // 定義成功的數據提交后會發生什么 XHR.addEventListener("load", function(event) { alert(event.target.responseText); }); // 定義失敗的情況會發生什么 XHR.addEventListener("error", function(event) { alert("Oups! Something goes wrong."); }); // 配置請求 XHR.open("POST", "https://example.com/cors.php"); // 發送的數據是用戶在表單中提供的 XHR.send(FD); } // 訪問表單元素 var form = document.getElementById("myForm"); // 并接管其submit事件 form.addEventListener("submit", function (event) { event.preventDefault(); sendData(); }); });
結果如下:
效果
若你在一個含有組件的表單中使用FormData對象,那么數據會被自動處理。但要手動發送二進制數據的話,還有很多額外工作要做。
現代Web有很多二進制數據源:比如FileReader、Canvas、WebRTC。但不幸的是,某些老舊瀏覽器不能訪問二進制數據或者需要復雜的方法才能實現。這些遺留的問題已不在本文討論范圍之內。若你想了解更多關于FileReader API,請閱讀Using files from web applications。
用FormData來發送二進制是很直接的,使用append()方法就好了。但要手動做到這點,就需要一些技巧了。
在下面的例子中,我們會用來FileReader API來訪問二進制數據,然后手動建立多部分的表單數據請求。
如你所見,HTML用了標準的,這沒什么神奇的。“神奇”的部分在JavaScript里:
// 由于我們要訪問DOM結點,所以得在頁面加載完后才初始化腳本 window.addEventListener("load", function () { // 這些變量用來存儲表單數據 var text = document.getElementById("i1"); var file = { dom : document.getElementById("i2"), binary : null }; // 使用FileReader API來訪問文件內容 var reader = new FileReader(); // 由于FileReader是異步的,所以得在其完成文件讀取后才存儲結果 reader.addEventListener("load", function () { file.binary = reader.result; }); // 在頁面加載時,若已經選擇了文件就直接讀取它 if(file.dom.files[0]) { reader.readAsBinaryString(file.dom.files[0]); } // 否則在用戶選擇文件時再讀取它 file.dom.addEventListener("change", function () { if(reader.readyState === FileReader.LOADING) { reader.abort(); } reader.readAsBinaryString(file.dom.files[0]); }); // sendData是本例的主要函數 function sendData() { // 若已經選擇了文件,就等瀏覽器讀取完 // 否則就延遲本函數的執行 if(!file.binary && file.dom.files.length > 0) { setTimeout(sendData, 10); return; } // 要構建多部分的表單數據請求,需要一個XMLHttpRequest實例 var XHR = new XMLHttpRequest(); // 需要一個分隔符來定義請求體的每部分 var boundary = "blob"; // 將請求體存為一個字符串 var data = ""; // 若用戶選擇了文件 if (file.dom.files[0]) { // 開啟請求體的新部分 data += "--" + boundary + " "; // 該部分描述為表單數據 data += "content-disposition: form-data; " // 定義表單數據的名字 + "name="" + file.dom.name + ""; " // 提供真實的文件名 + "filename="" + file.dom.files[0].name + "" "; // 提供文件的MIME類型 data += "Content-Type: " + file.dom.files[0].type + " "; // 元數據和真實數據部分間有一個空行 data += " "; // 往請求體里添加二進制數據 data += file.binary + " "; } // 文本數據的組織更加簡單 // 開啟請求體的新部分 data += "--" + boundary + " "; // 描述為表單數據并命名 data += "content-disposition: form-data; name="" + text.name + "" "; // 元數據和真實數據部分間有一個空行 data += " "; // 往請求體里添加文本數據 data += text.value + " "; // 完成了所有部分,就“閉合”請求體 data += "--" + boundary + "--"; // 定義成功的數據提交后會發生什么 XHR.addEventListener("load", function(event) { alert("Yeah! Data sent and response loaded."); }); // 定義失敗的情況會發生什么 XHR.addEventListener("error", function(event) { alert("Oups! Something went wrong."); }); // 配置請求 XHR.open("POST", "https://example.com/cors.php"); // 添加必要的HTTP請求頭來處理多部分表單數據的POST請求 XHR.setRequestHeader("Content-Type","multipart/form-data; boundary=" + boundary); // 最后,發送數據 XHR.send(data); } // 訪問表單 var form = document.getElementById("myForm"); // 接管submit事件 form.addEventListener("submit", function (event) { event.preventDefault(); sendData(); }); });
結果如下:
效果
瀏覽器的不同,導致通過JavaScript發送表單數據可以簡單或很困難。FromData對象是通常的解決方案,而且我們應該毫不猶豫地在老舊瀏覽器上使用其polyfill:
這個gist用Web Workers來提供FromData
HTML5-formdata致力于提供FromData對象的polyfill,但這得依賴File API
這個polyfill提供了FromData的大部分新方法(entries, keys, values和對for...of的支持)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/50957.html
摘要:能異步地發送任意數據的技術稱為,表示異步的和。若你使用,使用發送表單還會影響同源策略,并導致內容被發送到一個無法訪問的中。但要手動發送二進制數據的話,還有很多額外工作要做。用來發送二進制是很直接的,使用方法就好了。 系列文章說明 原文 在[發送表單數據]()一文中,HTML表單可以聲明式地發送一個HTTP請求。但表單也可以用JavaScript來準備一個HTTP請求。本文將探索如何...
摘要:用表單中創建一個對象要用現有的元素建立一個對象,可以在建立對象時傳入指定的元素。通過提交表單和發送文件而不使用對象若你想了解如何基于進行序列化和發送表單操作,而不使用對象,可閱讀此文。 系列文章說明 原文 FormData對象能讓你生成一系列用于XMLHttpRequest發送的鍵值對。它主要的目的在于發送表單數據,但也能獨立用于傳輸有鍵形式的數據。其傳輸的數據格式和表單使用sub...
摘要:用表單中創建一個對象要用現有的元素建立一個對象,可以在建立對象時傳入指定的元素。通過提交表單和發送文件而不使用對象若你想了解如何基于進行序列化和發送表單操作,而不使用對象,可閱讀此文。 系列文章說明 原文 FormData對象能讓你生成一系列用于XMLHttpRequest發送的鍵值對。它主要的目的在于發送表單數據,但也能獨立用于傳輸有鍵形式的數據。其傳輸的數據格式和表單使用sub...
摘要:一個表單由一或多個部件組成,這些部件可以是文本框單行或多行選擇框按鈕復選框或單選按鈕。在我們的示例里,一個文本框中用了該屬性的默認值,該值表示一個基本的單行文本框,用于接收無控制或驗證的任何文本。 前言 這個系列譯自mdn上的一份表單指南,原文詳盡闡述了表單相關的基礎知識。而表單作為一個經典的頁面交互方式,是每個前端工程師繞不開的話題,通過翻譯這個系列的文章既是有助于掃清自己的知識盲區...
摘要:提到老舊瀏覽器,我們腦海中往往復現的就是舊版的。但幸運的是,有一些技巧可以協助解決由老舊瀏覽器引起的的問題。放棄表單和老舊瀏覽器的最大問題是對的支持。結論如你所見,處理老舊瀏覽器所涉及的內容不止有表單。 系列文章說明 原文 所有的web開發者都會很快(或者很痛苦地)意識到Web是一個粗糙的環境,其中最糟糕的一點就是老舊的瀏覽器。提到老舊瀏覽器,我們腦海中往往復現的就是舊版的IE。但...
閱讀 3090·2021-11-22 13:54
閱讀 842·2021-11-04 16:08
閱讀 4540·2021-10-11 11:09
閱讀 3605·2021-09-22 16:05
閱讀 936·2019-08-30 15:54
閱讀 397·2019-08-30 15:44
閱讀 603·2019-08-30 14:05
閱讀 1025·2019-08-30 12:46