www.baike369.com
百科369 > Ajax教程 > XMLHttpRequest(XHR)对象的方法的愚蠢行为

XMLHttpRequest(XHR)对象的方法的愚蠢行为


XMLHttpRequest(XHR)对象的方法的愚蠢行为

XMLHttpRequest规范指明用户代理(user-agent)支持的XHR必须支持的HTTP方法有:GET、POST、HEAD、PUT、DELETE和OPTIONS等。但是,它也规定了应当支持任何可以允许的方法,这包含了各种WebDAV的方法,比如MOVE、PROPFIND、PROPPATCH、MKCOL、COPY、LOCK、UNLOCK、POLL等和其它的方法。

从理论上说,甚至可以有自己的方法,尽管一般来说这在Web上是不安全的做法,因为它可能会在传输过程中被缓存或者遇到的Web应用程序防火墙过滤掉。甚至尽量避免采用了任何太激进的方式,但在不同的浏览器中使用XHR对GET、POST和HEAD等方法进行测试时,仍然可以发现这些结果多少有些不一致。

某些浏览器,比如Opera和Safari等,会拒绝大多数的扩展方法,如果没有理解或者支持这些方法的话,就会将其转换成GET的形式。这是非常糟糕的处理方式,因为它可能触发服务器端的问题,并且可能产生完全无法预料的行为。在使用IE的情况下,当尝试提供它不知道的方法时,它将会抛出错误。这是一个更合理的处理方式,尽管根据规范,这仍然是错误的。另一方面,IE支持所有的WebDAV方法,并且这些方法广泛地应用在Outlook Web Access中。Firefox似乎是最接近最新规范的浏览器,它允许其它的方法,包括WebDAV方法或者甚至是自定义的方法,因此,我们必须要有一个有能力处理任何自定义方式创建的方法的服务器。


实例

本例可以了解我们的浏览器当前支持哪些方法。

1. 创建XML文档,比如1.xml。

2. baike369.php的源代码如下:

<?php
  $fileName = rand() . "1.xml";
  $fh = fopen("" . $fileName, 'w') or die("can't open file");
  $message = 
  "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n
    <pollresults>\r\n
    <rating>1</rating>\r\n
    <average>3</average>\r\n
    <votes>100</votes>\r\n
    </pollresults>\r\n
  " ;
  fwrite($fh, $message);
  fclose($fh);
  $dirName = rand();
  $curDirectory = "userfiles";
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>XMLHttpRequest(XHR)对象的方法的愚蠢行为-www.baike369.com</title>
<style type="text/css"> 
  body {font-family: Verdana, Arial, Sans-serif;}
  h1 {text-align: center;}
  .deleteButton {background-color: transparent; color: #990000; font-weight: bold; font-size: larger; border-style: hidden; margin-left: 5px;}
  #responseOutput {border-top-style: dashed; border-top-width: 2px;}
</style>
<script type="text/javascript"> 
var fileName = "<?php echo $fileName; ?>";
var dirName = "<?php echo $dirName; ?>";
var curDirectory = "<?php echo $curDirectory; ?>";
var lockToken = "";
function escapeValue(value)
{
  var escapedVal = (encodeURIComponent) ? encodeURIComponent(value) : escape(value);
  return escapedVal.replace(/\%20/g,'+');
}
function createXHR()
{
  try { return new XMLHttpRequest(); } catch(e) {}
  try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
  try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
  try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
  try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
  return null;
}
function sendRequest(form)
{
  var url = "http://www.baike369.com/code.php";
  var responseOutput = document.getElementById("responseOutput");
  responseOutput.innerHTML = "";
  var xhr = createXHR();
  if (xhr)
  {
    var postBody = null;
    //get payload
    var payload = "";
    for (var i=0; i < g_payloadList.length;i++)
    {
      if (payload != "")
        payload += "&";
      var id = g_payloadList[i].id.substring(3);
      var key = document.getElementById(("inputname" + id)).value.replace(/^\s+|\s+$/g, "");
      if (key != "")
      {
        var val = document.getElementById(("inputvalue" + id)).value;
        payload += escapeValue(key) + "=" + escapeValue(val);
      }
    }    
    var requestMethod = form.requestMethod.options[form.requestMethod.selectedIndex].text;
    switch (requestMethod)
    {
      case "GET":
        url += "?" + payload;
        break;
      case "LOCK":
      case "UNLOCK":
      case "MOVE":
      case "PROPFIND":
      case "PROPPATCH":
      case "COPY":
        url = "http://www.baike369.com/webdav/" + curDirectory + "/" + fileName;
        break;
      case "MKCOL":
        url = "http://www.baike369.com/webdav/userfiles/" + dirName;
        break;
      case "OPTIONS" : url = "http://baike369.com/*";
        break;
    }
    var async = form.async.checked;
    xhr.open(requestMethod, url, async);
    switch(requestMethod)//yes again
    {
      case "POST":
        postBody = payload;               
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        break;
      case "COPY":
        xhr.setRequestHeader("Overwrite", "T");
        //intentially fall through
      case "MOVE":
        (curDirectory == "userfiles") ? curDirectory = "userfiles/move"  : curDirectory = "userfiles"; 
        xhr.setRequestHeader("Destination", "http://www.baike369.com/webdav/" + curDirectory + "/" + fileName);
        break;
      case "LOCK":
        postBody = "<";
        postBody += "?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
        postBody += "<d:lockinfo xmlns:d=\"DAV:\">\n";
        postBody += "<d:lockscope><d:exclusive/></d:lockscope>\n";
        postBody += "<d:locktype><d:write/></d:locktype>\n";
        postBody += "</d:lockinfo>\n";
        break;
      case "UNLOCK":
        postBody = "<";
        postBody += "?xml version=\"1.0\" ?>\n";
        postBody += "<D:transactioninfo xmlns:D=\"DAV:\">\n";
        postBody += "<D:transactionstatus><D:commit/></D:transactionstatus>\n";
        postBody += "</D:transactioninfo>\n";
        xhr.setRequestHeader("Lock-Token", lockToken);
        break;
      case "PROPFIND":
        postBody = "<";
        postBody += "?xml version=\"1.0\"?>\n";
        postBody += "<a:propfind xmlns:a=\"DAV:\">\n";
        postBody += "<a:prop><a:getcontenttype/></a:prop>\n";
        postBody += "</a:propfind>\n";
        xhr.setRequestHeader("Depth", 0);
        xhr.setRequestHeader("Translate", "f");
        break;
      case "PROPPATCH":
        postBody = "<";
        postBody += "?xml version=\"1.0\"?>\n";
        postBody += "<d:propertyupdate xmlns:d=\"DAV:\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n";
        postBody += "<d:set>\n";
        postBody += "<d:prop>\n";
        postBody += "<o:Author>Thomas Powell</o:Author>\n";
        postBody += "</d:prop>\n";
        postBody += "</d:set>\n";
        postBody += "</d:propertyupdate>\n";
        break;
      }
      //set request headers
      for (var i=0; i < g_customHeadersList.length;i++)
      {
        var id = g_customHeadersList[i].id.substring(3);
        var key = document.getElementById(("inputname" + id)).value.replace(/^\s+|\s+$/g, "");
        if (key != "")
        {
          var val = document.getElementById(("inputvalue" + id)).value;
          xhr.setRequestHeader(key, val);
        }
      }
      for (var i=0; i < g_standardHeadersList.length;i++)
      {
        var id = g_standardHeadersList[i].id.substring(3);
        var sel = document.getElementById(("selectname" + id));
        var key =  sel.options[sel.selectedIndex].text;
        var val = document.getElementById(("inputvalue" + id)).value;
        xhr.setRequestHeader(key, val);
      }
      if (async)
        xhr.onreadystatechange = function(){handleResponse(xhr,requestMethod,postBody);};
      xhr.send(postBody);
    } 
    if (!async)
      handleResponse(xhr);
}
function handleResponse(xhr, requestMethod, postBody)
{
  if (xhr.readyState == 4)
  {
    if (xhr.status == 200)
    {
      var responseString, requestString;
      var responseOutput = document.getElementById("responseOutput");
      var converted = xhr.getAllResponseHeaders().replace(/<([^>]*)>/g, "&lt;$1&gt;");
      converted = converted.replace(/\n/g, "<br/>");
      switch(requestMethod)
      {
        case "LOCK":
          requestString = '<strong>Payload</strong><div class="data">' + postBody.replace(/<([^>]*)>/g, "&lt;$1&gt;").replace(/\n/g, "<br/>") + '</div>';
          responseString = '<div class="data">' + xhr.responseText.replace(/<([^>]*)>/g, "&lt;$1&gt;").replace(/\n/g, "<br/>") + '</div>';
          lockToken = xhr.getResponseHeader("Lock-Token");
          alert(lockToken);
          break;
        default: 
          requestString =    xhr.responseText;
          responseString = '<div class="data" title="In this example the payload is the network details">N/A</div>'; 
      }
      responseOutput.innerHTML = '<h3>Request</h3>' + requestString;
      responseOutput.innerHTML += '<h3>Response</h3><strong>Headers</strong><div class="data">' + converted + '</div><strong>Payload</strong>' + responseString;
      responseOutput.style.display = "";
    }
    else 
    {
      var responseOutput = document.getElementById("responseOutput");
      var converted = xhr.getAllResponseHeaders();
      if (converted)
      {
        converted = converted.replace(/<([^>]*)>/g, "&lt;$1&gt;");
        converted = converted.replace(/\n/g, "<br/>");
      }
      if (postBody)
        responseOutput.innerHTML = '<h3>Request</h3><strong>Payload</strong><div class="data">' + postBody.replace(/<([^>]*)>/g, "&lt;$1&gt;").replace(/\n/g, "<br/>") + '</div>';
      else
        responseOutput.innerHTML = '';
      responseOutput.innerHTML += '<h3>Response</h3><strong>Status</strong><div class="data"> ' + xhr.status + " " + xhr.statusText + '</div>';
      responseOutput.innerHTML += '<strong>Headers</strong><div class="data">' + converted + '</div>';
      responseOutput.innerHTML += '<strong>Payload</strong><div class="data">' + xhr.responseText.replace(/<([^>]*)>/g, "&lt;$1&gt;").replace(/\n/g, "<br/>") + '</div>';
      responseOutput.style.display = "";        
    }
  }
}
var g_rowCount = 1;
var g_payloadList  = new Array();
var g_standardHeadersList  = new Array();
var g_customHeadersList  = new Array();
function makeNewRow(headerRowName, buttonRowName, addSelect, list)
{
  var buttonRow = document.getElementById(buttonRowName);
  var parentTable = buttonRow.parentNode;
  var headerRow = document.getElementById(headerRowName);
  /* build a container */
  var row = document.createElement("tr");
  row.id = "row" + g_rowCount;
  var td = document.createElement("td");
  if (addSelect)
  {
    /* Add the select filed */
    var duplicateSelect = document.getElementById("duplicateSelect");
    var requestHeaderSelect = duplicateSelect.cloneNode(true);
    requestHeaderSelect.id = "selectname" + g_rowCount;
    requestHeaderSelect.name = "selectname" + g_rowCount;
    requestHeaderSelect.style.visibility = "visible";
    td.appendChild(requestHeaderSelect);
  }
  else
  {
    /* add text input name field */
    var inputField = document.createElement("input");
    inputField.type = "text";
    inputField.size = "40";
    inputField.id = "inputname" + g_rowCount;
    inputField.name = "inputname" + g_rowCount;
    td.appendChild(inputField);
  }
  row.appendChild(td);
  td = document.createElement("td");
  /* add text input value field */
  var valueField = document.createElement("input");
  valueField.type = "text";
  valueField.size = "40";
  valueField.id = "inputvalue" + g_rowCount;
  valueField.name = "inputvalue" + g_rowCount;
  td.appendChild(valueField);
  row.appendChild(td);
  /* add a remove button */
  td = document.createElement("td");
  var deleteButton = document.createElement("img");
  deleteButton.src = "images/201406/2972.gif";
  deleteButton.className = "deleteButton";
  deleteButton.alt = "X";
  deleteButton.onclick =  function(){removeRow(row, list);};
  td.appendChild(deleteButton);
  row.appendChild(td);
  headerRow.style.display = "";
  /* add particular the controls */
  parentTable.insertBefore(row, buttonRow);    
  /* update our counts */
  list.push(row);
  g_rowCount++;
}
function removeRow(row, list)
{
  /* remove from array */
  for (var i=0; i < list.length;i++)
    if (list[i].id == row.id)
      list.splice(i, 1);
  /* remove form control */
  var parentTable = row.parentNode;
  parentTable.removeChild(row);
}
function updateFields(methodSelect)
{
  var requestMethod = methodSelect.options[methodSelect.selectedIndex].text;
  var payload = document.getElementById("payload");
  if (requestMethod != "GET" && requestMethod != "POST")
    payload.style.visibility = "hidden";
  else
    payload.style.visibility = "visible";
}
function updateDAV(davCheck, requestMethod)
{
  if (davCheck.checked)
  {
    var davOptions = ["MOVE", "PROPFIND", "PROPPATCH", "MKCOL", "COPY", "LOCK", "UNLOCK"];
    for (var i=0;i<davOptions.length;i++)
    {
      var option = document.createElement("option");
      var txt = document.createTextNode(davOptions[i]);
      option.appendChild(txt)
      requestMethod.appendChild(option);
    }    
  }
  else
  {
    for (var i=13;i>6;i--)
      requestMethod.remove(i);
  }
}
window.onload = function () 
{ 
  document.requestForm.requestButton.onclick = function () { sendRequest(this.form); };
  document.getElementById('payloadButton').onclick = function(){makeNewRow('payloadHeader', 'payloadButtonRow', false, g_payloadList);};
  document.getElementById('standardHeadersButton').onclick = function(){makeNewRow('standardHeadersHeader', 'standardHeadersButtonRow', true, g_standardHeadersList);};
  document.getElementById('customHeadersButton').onclick = function(){makeNewRow('customHeadersHeader', 'customHeadersButtonRow', false, g_customHeadersList);};
  document.requestForm.requestMethod.onchange = function(){updateFields(this);};
  document.requestForm.DAV.onchange = function(){updateDAV(this, this.form.requestMethod);};
};
</script> 
</head>
<body>
<h1>Ajax Request Explorer</h1>
<form action="#" name="requestForm">
<fieldset>
<legend>Basic</legend>
<label>HTTP Method
  <select name="requestMethod">
    <option>GET</option>
    <option>HEAD</option>
    <option>POST</option>
    <option>PUT</option>
    <option>DELETE</option>
    <option>OPTIONS</option>
    <option>TRACE</option>
    <option>NOTMETHOD</option>
   </select>
</label>
<label>&nbsp;&nbsp;Allow WebDAV: <input type="checkbox" name="DAV" /></label>
<br />
<label>URL: <input type="text" name="url" size="40" value="http://www.baike369.com/code.php" readonly="readonly" name="queryString" /></label><br />
<label>Asynchronous:
<input type="checkbox" name="async" value="true" checked="checked" /> 
</label>
</fieldset>
<br /><br />
<div id="StandardHeaders">
<fieldset>
<legend>Standard Request Headers</legend>
  <table>        
    <tr id="standardHeadersHeader"  style="display:none;">
      <th>Name</th><th colspan="2">Value</th>
    </tr>    
    <tr id="standardHeadersButtonRow">
      <td colspan="3"><img src="images/201406/2973.gif" id="standardHeadersButton" alt="Add Standard Header" /></td>
    </tr>        
  </table>
 </fieldset>
</div>
<br /><br />
<div id="CustomHeaders">
<fieldset>
<legend>Custom Request Headers</legend>
  <table>        
    <tr id="customHeadersHeader"  style="display:none;">
      <th>Name</th><th colspan="2">Value</th>
    </tr>    
    <tr id="customHeadersButtonRow">
      <td colspan="3">
        <img src="images/201406/2973.gif" id="customHeadersButton" alt="Add Custom Request Header" />
      </td>
    </tr>        
  </table>
</fieldset> 
</div>
<br /><br />
<div id="payload">
<fieldset>
<legend>Payload</legend>
  <table>        
    <tr id="payloadHeader" style="display:none;">
      <th>Name</th><th colspan="2">Value</th>
    </tr>    
    <tr id="payloadButtonRow">
      <td colspan="3" >
        <img src="images/201406/2973.gif" id="payloadButton" alt="Add name-value pair" />
      </td>
    </tr>        
  </table>
</fieldset> 
</div>
<br /><br /><br />
  <input type="button" name="requestButton" value="Send Request" />
  <select id="duplicateSelect" style="visibility:hidden;">
    <option>Accept</option>
    <option>Accept-Charset</option> 
    <option>Accept-Encoding</option>
    <option>Accept-Language</option>
    <option>Authorization</option>
    <option>Cache-Control</option>
    <option>Charge-To</option> 
    <option>Connection</option>
    <option>Content-Base</option>
    <option>Content-Language</option> 
    <option>Content-Encoding</option>
    <option>Content-Length</option> 
    <option>Content-Location</option> 
    <option>Content-MD5</option> 
    <option>Content-Range</option> 
    <option>Content-Type</option> 
    <option>Content-Version</option> 
    <option>Host</option>
    <option>Date</option>
    <option>Delta-Base</option> 
    <option>Depth</option> 
    <option>Destination</option> 
    <option>ETag</option> 
    <option>Expect</option>
    <option>From</option> 
    <option>If-Match</option>
    <option>If-Modified-Since</option>
    <option>If-None-Match</option> 
    <option>If-Range</option> 
    <option>If-Unmodified-Since</option> 
    <option>Keep-Alive</option>
    <option>Max-Forwards</option> 
    <option>MIME-Version</option> 
    <option>Overwrite</option>
    <option>Pragma</option> 
    <option>Proxy-Authorization</option>
    <option>Range</option> 
    <option>Referer</option>
    <option>SOAPAction</option> 
    <option>TE</option>
    <option>Timeout</option> 
    <option>Trailer</option>
    <option>Transfer-Encoding</option> 
    <option>Upgrade</option>
    <option>User-Agent</option>
    <option>Via</option>
    <option>Warning</option>
  </select>
</form>
<br />
<div id="responseOutput" style="display:none;">&nbsp;</div>
</body>
</html>

3. code.php的源代码如下:

<?php
    header("Cache-Control: no-cache");
    header("Pragma: no-cache");
    $responseStatus = gpc("responsestatus");
    if ($responseStatus == "200")
        header("HTTP/1.1 200 OK\n\n");
    else if ($responseStatus == "204")
        header("HTTP/1.1 204 No Content\n\n");
    else if ($responseStatus == "403")
        header("HTTP/1.1 403 Forbidden\n\n");
    else if ($responseStatus == "404")
        header("HTTP/1.1 404 Not Found\n\n");
    else if ($responseStatus == "500")
        header("HTTP/1.1 500 Internal Server Error\n\n");
    else if ($responseStatus == "501")
        header("HTTP/1.1 501 Not Implemented\n\n");
    else if ($responseStatus == "502")
        header("HTTP/1.1 502 Bad Gateway\n\n");
    else if ($responseStatus == "503")
        header("HTTP/1.1 503 Service Unavailable\n\n");
    else if ($responseStatus == "504")
        header("HTTP/1.1 504 Gateway Timeout\n\n");
    else if ($responseStatus == "505")
        header("HTTP/1.1 505 HTTP Version Not Supported\n\n");
    if ($responseStatus && $responseStatus != "200")exit;    
    $headers = getallheaders();
    echo "<strong>Headers</strong><div class='data'>";
    foreach($headers as $key => $value)
        echo "$key: $value <br />";
    echo "</div>";
    echo "<strong>Payload</strong><div class='data'>";
    $varList = "";
    $varString = "";
    if (count($_GET) > 0)
    {
        $varString = "GET Query String: ";
        $varString .= $_SERVER['QUERY_STRING'];
        foreach($_GET as $key => $value) 
        {
            $varList .= "$key = $value <br>\n";
        }
        $varString .= "<br />";
    }
    else if (count($_POST) > 0)
    {
        $varString = "POST Input: ";
        foreach($_POST as $key => $value) 
        {
            if ($varString != "POST Input: ")
                $varString .= "&";
            $varString .= urlencode($key) . "=" . urlencode($value);
            $varList .= "$key = $value <br>\n";
        }
        $varString .= "<br />";
    }
    else
        $varString = "N/A";
    echo $varString;
    echo $varList;
    echo "</div>";
/* Helper functions */
function gpc($name)
{
    if (isset($_GET[$name]))
    return $_GET[$name];
    else if (isset($_POST[$name]))
    return $_POST[$name];
    else if (isset($_COOKIE[$name]))
    return $_COOKIE[$name];
    else
    return "";
}
?>

4. 显示效果如下:

XMLHttpRequest(XHR)对象的方法的愚蠢行为

可以使用该程序来设置任何类型的方法、报头和负载的组合。

Copyright© 2011-2016 www.baike369.com All Rights Reserved