ارسال فرم HTML با جاوا اسکریپت — راهنمای جامع
در این مقاله میخواهیم به بررسی روشهای ارسال فرم HTML از طریق جاوا اسکریپت بپردازیم. در یکی از بخشهای قبلی این سری مطالب دیدیم که فرمهای HTML را میتوان به صورت یک درخواست HTML به روش اعلانی ارسال کرد. در این بخش نیز با روش آمادهسازی یک درخواست HTML برای ارسال از طریق جاوا اسکریپت آشنا میشویم. برای مطالعه بخش قبلی این مجموعه مطالب آموزشی HTML روی لینک زیر کلیک کنید:
فرم همیشه فرم نیست
از زمان معرفی open Web apps به این سو، فرم های HTML به چیزی فراتر از فرمهای معمولی که به صورت کاغذی پر میکنیم تبدیل شدهاند. بدین ترتیب توسعهدهندگان رفتهرفته شروع به دست گرفتن کنترل بیشتری روی فرایند انتقال دادهها کردهاند.
به دست گرفتن کنترل اینترفیس سراسری
فرم HTML استاندارد، URL-ی را بارگذاری میکند که دادهها در آن ارسال میشوند، یعنی پنجره مرورگر برای بارگذاری یک صفحه کامل اقدام میکند. با این حال اجتناب از بارگذاری یک صفحه کامل موجب میشود که با پنهانسازی مشکلات و تأخیر شبکه، تجربه کاربری روانتری رقم بخورد.
اغلب UI-های مدرن از فرم های HTML برای گردآوری ورودیهای کاربر استفاده میکنند. زمانی که کاربر تلاش میکند دادهها را ارسال بکند، اپلیکیشن کنترل را به دست میگیرد و این دادهها را به صورت ناهمگام در پسزمینه ارسال میکند. در ادامه تنها بخشهایی از رابط کاربری که نیازمند تغییر هستند، بهروزرسانی میشوند.
فناوری ارسال دادههای دلخواه به صورت ناهمگام به نام AJAX شناخته میشود که اختصاری برای عبارت «جاوا اسکریپت و XML ناهمگام» (Asynchronous JavaScript And XML) است.
تفاوت استفاده از جاوا اسکریپت در چیست؟
AJAX از شیء DOM به نام XMLHttpRequest یا به اختصار XHR استفاده میکند. این شیء میتواند درخواستهای HTTP بسازد، آنها را ارسال کند و نتایج را بازگشت دهد.
نکته: تکنیکهای قدیمیتر AJAX صرفاً روی XMLHttpRequest تکیه نداشتند. برای نمونه JSOAP با تابع ()eval ترکیب شده بود. این تکنیک نیز کار میکند، اما استفاده از آن توصیه نمیشود، زیرا مشکلات امنیتی جدی دارد. تنها دلیل برای استفاده از این تکنیک ممکن است این باشد که مرورگرهای قدیمی از XMLHttpRequest یا JSON پشتیبانی نمیکنند، اما چنین مرورگرهایی باید واقعاً قدیمی باشند. بنابراین سعی کنید از چنین تکنیکهایی استفاده کنید.
XMLHttpRequest از لحاظ تاریخی برای واکشی و ارسال XML به عنوان یک فرمت تبادل داده طراحی شده است. اما نه XML و نه JSON در انکودینگ درخواست دادههای فرم جای نمیگیرند. دادههای فرم (application/x-www-form-urlencoded) لیستهای انکودشده URL از جفتهای کلید/مقدار تشکیل میدهند. برای ارسال کردن دادههای باینری، درخواست HTTP به صورت multipart/form-data تغییر شکل مییابد.
اگر فرانتاند (کدی که در مرورگر اجرا میشود) و کد بکاند (کدی که روی سرور اجرا میشود) را کنترل کنید، میتوانید JSON/XML را ارسال کرده و به هر شکلی که دوست دارید آنها را پردازش کنید.
اما اگر میخواهید از سرویس شخص ثالث استفاده کنید؛ کار به این سادگی نیست. برخی سرویسها تنها دادههای فرم را میپذیرند. همچنین مواردی وجود دارند که استفاده از دادههای فرم سادهتر است. اگر دادهها به صورت جفتهای کلید/مقدار، یا دادههای باینری خام باشند، ابزارهای بکاند موجود میتوانند آن را بدون نیاز به هیچ کد اضافی مدیریت کنند.
اینک سؤالی که مطرح میشود، این است که روش ارسال این دادهها چگونه است؟ در بخش بعدی به این سؤال پاسخ میدهیم.
ارسال دادههای فرم
3 روش برای ارسال دادههای فرم وجود دارد که از تکنیکهای قدیمی تا شیء جدیدتر FormData را شامل میشود. در ادامه همه این موارد را به تفصیل مورد بررسی قرار میدهیم.
ساخت XMLHttpRequest به صورت دستی
XMLHttpRequest سادهترین و مطمئنترین راه برای ایجاد درخواستهای HTTP محسوب میشود. برای ارسال دادههای فرم با استفاده از XMLHttpRequest باید ابتدا دادهها را از طریق انکودینگ URL آماده کنیم و از مشخصههای تعیین شده برای درخواستهای دادههای فرم پیروی نماییم. در ادامه مثال قبلی را بازنویسی میکنیم:
1<button type="button" onclick="sendData({test:'ok'})">Click Me!</button>
چنان که مشاهده میکنید، HTML در عمل تغییر نیافته است. با این حال، کد جاوا اسکریپت کاملاً متفاوت است:
1function sendData(data) {
2 var XHR = new XMLHttpRequest();
3 var urlEncodedData = "";
4 var urlEncodedDataPairs = [];
5 var name;
6
7 // Turn the data object into an array of URL-encoded key/value pairs.
8 for(name in data) {
9 urlEncodedDataPairs.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));
10 }
11
12 // Combine the pairs into a single string and replace all %-encoded spaces to
13 // the '+' character; matches the behaviour of browser form submissions.
14 urlEncodedData = urlEncodedDataPairs.join('&').replace(/%20/g, '+');
15
16 // Define what happens on successful data submission
17 XHR.addEventListener('load', function(event) {
18 alert('Yeah! Data sent and response loaded.');
19 });
20
21 // Define what happens in case of error
22 XHR.addEventListener('error', function(event) {
23 alert('Oops! Something goes wrong.');
24 });
25
26 // Set up our request
27 XHR.open('POST', 'https://example.com/cors.php');
28
29 // Add the required HTTP header for form data POST requests
30 XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
31
32 // Finally, send our data.
33 XHR.send(urlEncodedData);
34}
نتیجه نهایی به صورت زیر است:
نکته: استفاده از XMLHttpRequest تحت همان «سیاست اصالت» (origin policy) است که در زمان ارسال دادهها به وبسایتهای شخص ثالث مورد استفاده قرار میگیرد. در مورد درخواستهای cross-origin به کنترل دسترسی CORS و HTTP نیاز داریم.
استفاده از XMLHttpRequest و شیء FormData
ساخت یک درخواست HTTP به صورت دستی ممکن است کار پیچیدهای باشد. خوشبختانه یک استاندارد مشخصه XMLHttpRequest روشی راحت و ساده برای مدیریت درخواستهای دادههای فرم با شیء FormData ارائه میکند.
شیء FormData میتواند برای انتقال دادههای فرم یا برای به دست آوردن دادههای درون یک عنصر فرم جهت مدیریت برای ارسال کردن آن، مورد استفاده قرار گیرد. توجه کنید که اشیای FormData «فقط-نوشتنی» هستند، یعنی میتوان آنها را تغییر داد، اما نمیتوان محتوای آنها را بازیابی کرد.
در ادامه دو مثال از روش استفاده از این اشیا ارائه شده است:
استفاده از شیء مستقل FormData
1<button type="button" onclick="sendData({test:'ok'})">Click Me!</button>
شما احتمالاً با این کد ساده HTML آشنا هستید:
1function sendData(data) {
2 var XHR = new XMLHttpRequest();
3 var FD = new FormData();
4
5 // Push our data into our FormData object
6 for(name in data) {
7 FD.append(name, data[name]);
8 }
9
10 // Define what happens on successful data submission
11 XHR.addEventListener('load', function(event) {
12 alert('Yeah! Data sent and response loaded.');
13 });
14
15 // Define what happens in case of error
16 XHR.addEventListener('error', function(event) {
17 alert('Oops! Something went wrong.');
18 });
19
20 // Set up our request
21 XHR.open('POST', 'https://example.com/cors.php');
22
23 // Send our FormData object; HTTP headers are set automatically
24 XHR.send(FD);
25}
نتیجه آن چنین است:
استفاده از FormData متصل به یک عنصر فرم
روش دیگری که برای استفاده از شیء FormData وجود دارد، بهرهگیری از عنصر <form> است. بدین ترتیب یک FormData ایجاد میشود که دادههای موجود در فرم را نمایش میدهد.
کد نمونه HTML آن چنین است:
1<form id="myForm">
2 <label for="myName">Send me your name:</label>
3 <input id="myName" name="name" value="John">
4 <input type="submit" value="Send Me!">
5</form>
اما جاوا اسکریپت کنترل را از فرم میگیرد:
1window.addEventListener("load", function () {
2 function sendData() {
3 var XHR = new XMLHttpRequest();
4
5 // Bind the FormData object and the form element
6 var FD = new FormData(form);
7
8 // Define what happens on successful data submission
9 XHR.addEventListener("load", function(event) {
10 alert(event.target.responseText);
11 });
12
13 // Define what happens in case of error
14 XHR.addEventListener("error", function(event) {
15 alert('Oops! Something went wrong.');
16 });
17
18 // Set up our request
19 XHR.open("POST", "https://example.com/cors.php");
20
21 // The data sent is what the user provided in the form
22 XHR.send(FD);
23 }
24
25 // Access the form element...
26 var form = document.getElementById("myForm");
27
28 // ...and take over its submit event.
29 form.addEventListener("submit", function (event) {
30 event.preventDefault();
31
32 sendData();
33 });
34});
نتیجه کار چنین است:
شما با استفاده از مشخصه elements فرم میتوانید لیستی از همه عناصر دادهای را در فرم به دست آورید و به صورت دستی آنها را یک به یک مدیریت کنید. بدین ترتیب کنترل بیشتری روی این فرایند پیدا میکنید.
ساخت یک DOM در iframe پنهان
قدیمیترین روش برای ارسال ناهمگام دادههای فرم، ساخت یک فرم با استفاده از API مربوط به DOM است که دادهها را در یک عنصر پنهان < iframe> ارسال میکند. برای دسترسی به نتیجه ارسال باید محتوای < iframe> را بازیابی کنید.
هشدار: شما باید از این روش اجتناب کنید. در زمان استفاده از سرویسهای شخص ثالث این تکنیک موجب بروز ریسکهای امنیتی میشود، زیرا شما را در معرض حملههای تزریق اسکریپت قرار میدهد. اگر از HTTPS استفاده میکنید، این تکنیک میتواند روی same origin policy تأثیر بگذارد که محتوای یک <iframe> را غیر قابل دسترسی میکند. با این حال این متد در صورت نیاز به پشتیبانی از مرورگرهای خیلی قدیمی ممکن است تنها گزینه موجود باشد.
به مثال زیر توجه کنید:
1<button onclick="sendData({test:'ok'})">Click Me!</button>
1// Create the iFrame used to send our data
2var iframe = document.createElement("iframe");
3iframe.name = "myTarget";
4
5// Next, attach the iFrame to the main document
6window.addEventListener("load", function () {
7 iframe.style.display = "none";
8 document.body.appendChild(iframe);
9});
10
11// This is the function used to actually send the data
12// It takes one parameter, which is an object populated with key/value pairs.
13function sendData(data) {
14 var name,
15 form = document.createElement("form"),
16 node = document.createElement("input");
17
18 // Define what happens when the response loads
19 iframe.addEventListener("load", function () {
20 alert("Yeah! Data sent.");
21 });
22
23 form.action = "http://www.cs.tut.fi/cgi-bin/run/~jkorpela/echo.cgi";
24 form.target = iframe.name;
25
26 for(name in data) {
27 node.name = name;
28 node.value = data[name].toString();
29 form.appendChild(node.cloneNode());
30 }
31
32 // To be sent, the form needs to be attached to the main document.
33 form.style.display = "none";
34 document.body.appendChild(form);
35
36 form.submit();
37
38 // Once the form is sent, remove it.
39 document.body.removeChild(form);
40}
41Here's the live result:
نتیجه کد فوق چنین است:
کار با دادههای باینری
اگر از یک شیء FormData در فرمی با ویجتهای <"input type="file> استفاده میکنید، دادهها به صورت خودکار پردازش خواهند شد. اما برای ارسال دادههای باینری به صورت دستی باید کار بیشتری انجام یابد.
منابع زیادی برای دادههای باینری روی وب مدرن وجود دارند که برای نمونه شامل FileReader ،Canvas و WebRTC هستند. متأسفانه برخی مرورگرهای قدیمی نمیتوانند به دادههای باینری دسترسی پیدا کنند و نیازمند استفاده از راهحلهای پیچیدهای هستند. بررسی این مرورگرهای قدیمی خارج از حوصله این مقاله است.
ارسال دادههای باینری با پشتیبانی از FormData کار سرراستی است. کافی است از متد ()append استفاده کنید. اگر این کار را به صورت دستی انجام دهید پیچیدهتر خواهد بود.
ما در مثال زیر از API به نام FileReader برای دسترسی به دادههای باینری استفاده کردیم و سپس درخواست دادههای چندبخشی فرم را به صورت دستی میسازیم:
1<form id="myForm">
2 <p>
3 <label for="i1">text data:</label>
4 <input id="i1" name="myText" value="Some text data">
5 </p>
6 <p>
7 <label for="i2">file data:</label>
8 <input id="i2" name="myFile" type="file">
9 </p>
10 <button>Send Me!</button>
11</form>
چنان که مشاهده میکنید، کد HTML شامل یک <form> استاندارد است. در این کد هیچ اتفاق عجیبی نمیافتد. بخش اصلی کار در کد جاوا اسکریپت رخ میدهد:
1// Because we want to access DOM node,
2// we initialize our script at page load.
3window.addEventListener('load', function () {
4
5 // These variables are used to store the form data
6 var text = document.getElementById("i1");
7 var file = {
8 dom : document.getElementById("i2"),
9 binary : null
10 };
11
12 // Use the FileReader API to access file content
13 var reader = new FileReader();
14
15 // Because FileReader is asynchronous, store its
16 // result when it finishes to read the file
17 reader.addEventListener("load", function () {
18 file.binary = reader.result;
19 });
20
21 // At page load, if a file is already selected, read it.
22 if(file.dom.files[0]) {
23 reader.readAsBinaryString(file.dom.files[0]);
24 }
25
26 // If not, read the file once the user selects it.
27 file.dom.addEventListener("change", function () {
28 if(reader.readyState === FileReader.LOADING) {
29 reader.abort();
30 }
31
32 reader.readAsBinaryString(file.dom.files[0]);
33 });
34
35 // sendData is our main function
36 function sendData() {
37 // If there is a selected file, wait it is read
38 // If there is not, delay the execution of the function
39 if(!file.binary && file.dom.files.length > 0) {
40 setTimeout(sendData, 10);
41 return;
42 }
43
44 // To construct our multipart form data request,
45 // We need an XMLHttpRequest instance
46 var XHR = new XMLHttpRequest();
47
48 // We need a separator to define each part of the request
49 var boundary = "blob";
50
51 // Store our body request in a string.
52 var data = "";
53
54 // So, if the user has selected a file
55 if (file.dom.files[0]) {
56 // Start a new part in our body's request
57 data += "--" + boundary + "\r\n";
58
59 // Describe it as form data
60 data += 'content-disposition: form-data; '
61 // Define the name of the form data
62 + 'name="' + file.dom.name + '"; '
63 // Provide the real name of the file
64 + 'filename="' + file.dom.files[0].name + '"\r\n';
65 // And the MIME type of the file
66 data += 'Content-Type: ' + file.dom.files[0].type + '\r\n';
67
68 // There's a blank line between the metadata and the data
69 data += '\r\n';
70
71 // Append the binary data to our body's request
72 data += file.binary + '\r\n';
73 }
74
75 // Text data is simpler
76 // Start a new part in our body's request
77 data += "--" + boundary + "\r\n";
78
79 // Say it's form data, and name it
80 data += 'content-disposition: form-data; name="' + text.name + '"\r\n';
81 // There's a blank line between the metadata and the data
82 data += '\r\n';
83
84 // Append the text data to our body's request
85 data += text.value + "\r\n";
86
87 // Once we are done, "close" the body's request
88 data += "--" + boundary + "--";
89
90 // Define what happens on successful data submission
91 XHR.addEventListener('load', function(event) {
92 alert('Yeah! Data sent and response loaded.');
93 });
94
95 // Define what happens in case of error
96 XHR.addEventListener('error', function(event) {
97 alert('Oops! Something went wrong.');
98 });
99
100 // Set up our request
101 XHR.open('POST', 'https://example.com/cors.php');
102
103 // Add the required HTTP header to handle a multipart form data POST request
104 XHR.setRequestHeader('Content-Type','multipart/form-data; boundary=' + boundary);
105
106 // And finally, send our data.
107 XHR.send(data);
108 }
109
110 // Access our form...
111 var form = document.getElementById("myForm");
112
113 // ...to take over the submit event
114 form.addEventListener('submit', function (event) {
115 event.preventDefault();
116 sendData();
117 });
118});
نتیجه کد فوق چنین است:
سخن پایانی
بسته به مرورگری که استفاده میکنید، ارسال کردن دادهها از طریق جاوا اسکریپت میتواند کاری آسان یا دشوار باشد. شیء FormData عموماً پاسخگو است و روی مرورگرهای قدیمی نیز میتوان از یک polyfill برای آن استفاده کرد:
- این Gist (+) اقدام به پلیفیل کردن FormData با استفاده از Web Workers میکند.
- HTML5-formdata (+) اقدام به پلیفیل کردن شیء FormData میکند، اما نیازمند File API است.
- این پلیفیل (+) اغلب متدهای جدید FormData را دارد (مداخل، کلیدها، مقادیر و پشتیبانی از for...of)
برای مطالعه بخش این سری مقالات آموزشی روی لینک زیر کلیک کنید:
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای طراحی سایت با HTML و CSS
- آموزش طراحی وب با HTML – مقدماتی
- مجموعه آموزشهای برنامهنویسی
- ۵ گام برای درک کدهای پایه HTML – آموزش مقدماتی
- آشنایی مقدماتی با HTML — به زبان ساده
==
سلام یه سوال داشتم الان وقتی درخواست باز شده فایل php که قرار دادین در ادرس دقیقا کجاست؟ فایل جدا باید بسازیم؟