DataAsyncBlock (DAB)
如何在不使用 Ajax 的情况下,将表单数据的异步块发送到远程服务器。
引言
本文描述了一种在不使用 Ajax 的情况下,以异步方式将数据发布到远程服务器的替代方法。
背景
我需要一种将数据块发送到远程服务器的方法,绕过 Ajax 的安全沙箱。经过对不同想法的尝试,我找到了一种实际可行的解决方案,并且实现起来相对容易。我称之为 DataAsyncBlock
或简称 DAB
。
它使用 JSONP
、JSON
、P3P
、PHP
、JavaScript
和 DOM
将脚本标签注入到网页头部,通过服务器 GET
请求发送数据块。我已经测试过,它似乎可以在多个浏览器上工作。它可以将无限量异步数据发送到任何具有处理无序数据块代码的远程服务器。由于数据以带有索引和长度的块发送,因此它也不会被浏览器 URI
最大长度限制所阻止。服务器可以处理异步数据并随机收集,然后使用索引标识符和密钥或会话将其重新组合。
安全
使用这种方法发送个人数据存在明确的顾虑。请注意,并仅在您的项目适当的情况下使用此代码。
客户端代码
以下是客户端发送数据到远程服务器所需的代码。
// Checks for null, undefined objects and empty strings.
function isnull(o){return (o==undefined||o==null||(typeof(o)=='string'&&o==''))?true:false;}
// For turning json to objects.
String.prototype.run = function(){ return (new Function('with(this) { return ' + this + '}' )).call({}); };
// Encodes data to be sent via querystring.
function dabsafe(s)
{
return encodeURIComponent(s).replace(/!/g, '%21').replace("/'/g", '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
}
// The dab sending method.
function dab(webpage, callback, data)
{
// Variables
var head = document.head || document.getElementsByTagName('head')[0];
var buffer = 50;
// Encode url
var m = dabsafe(data);
var ml = (m.length <= buffer) ? 1 : (Math.floor(m.length / buffer) + 1);
for(var i = 0; i < ml; i++)
{
var pos = i * buffer;
var d = m.substring(pos, pos + buffer);
// Create script child and append to head
var s = document.createElement('script');
s.id = callback + i;
s.src = webpage + '?format=json&i=' + (i+1) +
'&l=' + (ml+1) + '&c=' + callback +
'&d=' + d;
head.appendChild(s);
}
}
// The dab callback
function dabcallback(d)
{
if(isnull(d)){ return; }
// Create data object.
var s = d.run();
if(!isnull(s.e))
{
// Alert error message
alert(s.e);
return;
}
else
{
alert(s.s);
}
// Remove script
var o = document.getElementById('dabcallback' + s.i);
o.parentNode.removeChild(o);
}
var fruits = 'blackcurrant | redcurrant | gooseberry | tomato | eggplant | guava | ' +
'lucuma | chili pepper | pomegranate | kiwifruit | grape | cranberry | ' +
'blueberry | pumpkin | gourd | cucumber | melon | orange | lemon | lime | ' +
'grapefruit | blackberry | raspberry | boysenberry | pineapple | fig | ' +
'mulberry | hedge apple | apple | rose hip | strawberry';
dab('http://yoursite.com/serverside.php', 'dabcallback', fruits);
服务器端代码
以下是在PHP
中处理服务器上 GET
请求所需的代码。<?php
// Start session if one doesn't exist and setup dat packet array.
if(!$_SESSION){ session_start(); }
if(!isset($_SESSION['dat'])){ $_SESSION['dat'] = array(''=>''); }
// Header needed to parse and collect data.
header("Content-Type: application/javascript");
header('P3P: CP="NOI ADM DEV COM NAV OUR STP"');
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
// Checks for nulls
function isnull($data){ return (isset($data) && !empty($data)) ? false : true; }
// Encodes data for json response.
function utf8_encode_all($dat)
{
if (is_string($dat)) return str_replace("'", ''', str_replace('"', '"', utf8_encode($dat)));
if (!is_array($dat)) return $dat;
$ret = array();
foreach($dat as $i=>$d) $ret[$i] = utf8_encode_all($d);
return str_replace("'", ''', str_replace('"', '"', $ret));
}
// Send a response to a webpage.
function response($_callback, $_data, $_err, $_index)
{
$_callback = utf8_encode_all($_callback);
$_data = utf8_encode_all($_data);
$_err = utf8_encode_all($_err);
$_index = utf8_encode_all($_index);
$data = array("s" => $_data, "e" => $_err, "i" => $_index);
echo $_callback . "('" . json_encode($data) ."');";
}
// Process the form
function process()
{
// Variables
$id = '';
$err = '';
$status = '';
// Get request query
$l = (!isnull($_GET['l'])) ? intval($_GET['l']) : '';
$c = (!isnull($_GET['c'])) ? $_GET['c'] : '';
$d = (!isnull($_GET['d'])) ? $_GET['d'] : '';
$i = (!isnull($_GET['i'])) ? intval($_GET['i']) . '' : '';
if($i==1){ unset($_SESSION['dat']); $_SESSION['dat'] = array(''=>''); }
// Add buffer to array
$_SESSION['dat'][$i] = $d;
// Sort array and pop it into one string.
ksort($_SESSION['dat']);
$arr = implode($_SESSION['dat']);
// Decode all special characters
$arr = urldecode($arr);
// Check if total packets have been captured.
if(count($_SESSION['dat'])==$l)
{
try
{
if(isnull($arr)){ throw new Exception('Fruit is required!'); }
$status = ucwords($arr);
}
catch (Exception $e)
{
$err = $e->getMessage();
}
// Write response back.
response($c, $status, $err, $i);
// Remove the current session.
session_destroy();
}
}
// Start the process
process();
?>
运行代码
当您运行客户端代码时,它将以 50 个字符的块将 fruit 字符串发送到服务器。它将使用查询字符串通过GET
请求发送数据包。服务器将为传入的数据创建一个唯一的会话,并创建一个数组来保存数据包。一旦服务器收集到所有数据包,它将处理 fruit 字符串,将每个单词的首字母大写,并将字符串作为 JSON
对象发送到定义的回调方法。然后,回调将通过 JavaScript
警报显示它。这是一个简单的演示,但可以扩展以处理大型项目。在这个演示中,我将缓冲区设置为 50 个字符,以向您展示它是如何发送多个数据包的。大多数浏览器允许 GET
请求的 MAX URI
长度为 2048 个或更多字符。因此,根据需要更改设置。代码输出
Blackcurrant | Redcurrant | Gooseberry | Tomato | Eggplant | Guava | Lucuma | Chili Pepper | Pomegranate | Kiwifruit | Grape | Cranberry | Blueberry | Pumpkin | Gourd | Cucumber | Melon | Orange | Lemon | Lime | Grapefruit | Blackberry | Raspberry | Boysenberry | Pineapple | Fig | Mulberry | Hedge Apple | Apple | Rose Hip | Strawberry
历史
- 2013 年 9 月 20 日 - 创建 DAB 想法供公众使用。