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