65.9K
CodeProject 正在变化。 阅读更多。
Home

DataAsyncBlock (DAB)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (7投票s)

2014年1月5日

CPOL

2分钟阅读

viewsIcon

26754

downloadIcon

142

如何在不使用 Ajax 的情况下,将表单数据的异步块发送到远程服务器。

引言

本文描述了一种在不使用 Ajax 的情况下,以异步方式将数据发布到远程服务器的替代方法。

背景  

我需要一种将数据块发送到远程服务器的方法,绕过 Ajax 的安全沙箱。经过对不同想法的尝试,我找到了一种实际可行的解决方案,并且实现起来相对容易。我称之为 DataAsyncBlock 或简称 DAB

它使用 JSONPJSONP3PPHPJavaScriptDOM 将脚本标签注入到网页头部,通过服务器 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("'", '&#39;', str_replace('"', '&#34;', 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("'", '&#39;', str_replace('"', '&#34;', $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

历史

  1. 2013 年 9 月 20 日 - 创建 DAB 想法供公众使用。
© . All rights reserved.