实际上 - 有一种方法可以支持 POST 请求。
并且不需要代理服务器 - 只需要一个小型实用 HTML 页面,如下所述。
以下是如何有效地进行 POST 跨域调用,包括附加文件、多部分和全部:)
首先是以下步骤理解想法,之后 - 找到一个实现示例。
jQuery的JSONP是如何实现的,为什么不支持POST请求?
虽然传统的 JSONP 是通过创建脚本元素并将其附加到 DOM 中来实现的,但结果会迫使浏览器触发 HTTP 请求来检索标签的源,然后将其作为 JavaScript 执行,即浏览器触发的 HTTP 请求很简单 GET。
什么不限于 GET 请求?
表单。提交表格时指定action
跨域服务器。
可以完全使用脚本创建 FORM 标记,使用脚本填充所有字段,设置所有必要的属性,注入 DOM,然后提交 - 全部使用脚本。
但是我们怎样才能在不刷新页面的情况下提交FORM呢?
我们指定target
表单到同一页面中的 IFRAME。
还可以使用脚本创建、设置、命名 IFRAME 并将其注入到 DOM。
但是我们如何才能向用户隐藏这项工作呢?我们将使用以下方法将 FORM 和 IFRAME 包含在隐藏的 DIV 中style="display:none"
(这是该技术中最复杂的部分,请耐心等待)
但是来自另一个域的 IFRAME 无法对其顶级文档调用回调。如何克服呢?
事实上,如果 FORM 提交的响应是来自另一个域的页面,则顶级页面和 IFRAME 中的页面之间的任何脚本通信都会导致“访问被拒绝”。所以服务器无法使用脚本回调。服务器能做什么?redirect。服务器可以重定向到任何页面 - 包括与顶级文档相同域中的页面 - 可以为我们调用回调的页面。
服务器如何重定向?
两种方式:
- 使用客户端脚本,例如
<Script>location.href = 'some-url'</script>
- 使用 HTTP 标头。看:http://www.webconfs.com/how-to-redirect-a-webpage.php http://www.webconfs.com/how-to-redirect-a-webpage.php
那么我最终会得到另一页吗?它对我有什么帮助?
这是一个简单的实用程序页面,将在所有跨域调用中使用。实际上,这个页面实际上是一种代理,但它不是服务器,而是一个简单的静态 HTML 页面,任何拥有记事本和浏览器的人都可以使用。
该页面所需要做的就是使用来自服务器的响应数据调用顶级文档上的回调。客户端脚本可以访问所有 URL 部分,并且服务器可以将其响应编码为其中的一部分,以及必须调用的回调的名称。意味着 - 该页面可以是静态页面和 HTML 页面,并且不必是动态服务器端页面:)
该实用程序页面将从它运行的 URL 中获取信息 - 特别是在下面的我的实现中 - 查询字符串参数(或者您可以使用锚 ID 编写自己的实现 - 即“#”右侧的 url 部分)符号)。由于此页面是静态的 - 甚至可以允许对其进行缓存:)
为每个 POST 请求添加 DIV、SCRIPT 和 IFRAME 最终不会泄漏内存吗?
如果您将其留在页面中 - 就会的。如果你在你之后清理 - 它不会。我们所要做的就是为 DIV 提供一个 ID,每当服务器响应到达或超时时,我们就可以使用该 ID 来删除 DIV 以及其中的 FORM 和 IFRAME。
我们得到什么?
有效地进行 POST 跨域调用,包括附加文件、多部分和全部:)
有什么限制?
- 服务器响应仅限于适合重定向的任何内容。
- 服务器必须始终向 POST 请求返回 REDIRECT。其中包括 404 和 500 错误。
或者 - 在触发请求之前在客户端上创建一个超时,这样您就有机会检测尚未返回的请求。
- 并不是每个人都能理解这一切以及所涉及的所有阶段。这是一种基础设施级别的工作,但是一旦你让它运行起来 - 它就会很震撼:)
我可以将其用于 PUT 和 DELETE 调用吗?
FORM 标签不进行 PUT 和 DELETE。
但这总比什么都没有好:)
好的,明白了概念。技术上是如何做到的?
我所做的是:
我创建了 DIV,将其设置为不可见,并将其附加到 DOM。我还给它一个 ID,我可以在服务器响应到达后从 DOM 中清理它(就像 JQuery 清理它的 JSONP SCRIPT 任务一样 - 但 DIV)。
Then I compose包含 IFRAME 和 FORM 的字符串 - 具有所有属性、属性和输入字段,并将其注入到不可见的 DIV 中。仅当 div 位于 DOM 中之后才将此字符串注入 DIV 中,这一点很重要。如果没有 - 它不会在所有浏览器上运行。
之后 - 我获取表格参考并提交。
只需记住之前的一行 - 设置一个超时回调如果服务器没有响应或以错误的方式响应。
回调函数包含清理代码。在响应超时的情况下,它也会被计时器调用(并在服务器响应到达时清除其超时计时器)。
显示代码!
下面的代码片段在“纯”javascript 上是完全“中性”的,并声明了它需要的任何实用程序。只是为了简化解释这个想法 - 它都在全局范围内运行,但是它应该更复杂一点......
尽可能将其组织在函数中,并参数化您需要的内容 - 但请确保需要相互查看的所有部分都在同一范围内运行:)
对于此示例 - 假设客户端运行在http://samedomain.com http://samedomain.com服务器运行在http://crossdomain.com http://crossdomain.com.
顶层文档中的脚本代码
//declare the Async-call callback function on the global scope
function myAsyncJSONPCallback(data){
//clean up
var e = document.getElementById(id);
if (e) e.parentNode.removeChild(e);
clearTimeout(timeout);
if (data && data.error){
//handle errors & TIMEOUTS
//...
return;
}
//use data
//...
}
var serverUrl = "http://crossdomain.com/server/page"
, params = { param1 : "value of param 1" //I assume this value to be passed
, param2 : "value of param 2" //here I just declare it...
, callback: "myAsyncJSONPCallback"
}
, clientUtilityUrl = "http://samedomain.com/utils/postResponse.html"
, id = "some-unique-id"// unique Request ID. You can generate it your own way
, div = document.createElement("DIV") //this is where the actual work start!
, HTML = [ "<IFRAME name='ifr_",id,"'></IFRAME>"
, "<form target='ifr_",id,"' method='POST' action='",serverUrl
, "' id='frm_",id,"' enctype='multipart/form-data'>"
]
, each, pval, timeout;
//augment utility func to make the array a "StringBuffer" - see usage bellow
HTML.add = function(){
for (var i =0; i < arguments.length; i++)
this[this.length] = arguments[i];
}
//add rurl to the params object - part of infrastructure work
params.rurl = clientUtilityUrl //ABSOLUTE URL to the utility page must be on
//the SAME DOMAIN as page that makes the request
//add all params to composed string of FORM and IFRAME inside the FORM tag
for(each in params){
pval = params[each].toString().replace(/\"/g,""");//assure: that " mark will not break
HTML.add("<input name='",each,"' value='",pval,"'/>"); // the composed string
}
//close FORM tag in composed string and put all parts together
HTML.add("</form>");
HTML = HTML.join(""); //Now the composed HTML string ready :)
//prepare the DIV
div.id = id; // this ID is used to clean-up once the response has come, or timeout is detected
div.style.display = "none"; //assure the DIV will not influence UI
//TRICKY: append the DIV to the DOM and *ONLY THEN* inject the HTML in it
// for some reason it works in all browsers only this way. Injecting the DIV as part
// of a composed string did not always work for me
document.body.appendChild(div);
div.innerHTML = HTML;
//TRICKY: note that myAsyncJSONPCallback must see the 'timeout' variable
timeout = setTimeout("myAsyncJSONPCallback({error:'TIMEOUT'})",4000);
document.getElementById("frm_"+id+).submit();
跨域的服务器来自服务器的响应预计是重定向,通过 HTTP 标头或通过编写 SCRIPT 标记。 (重定向更好,SCRIPT标签更容易用JS断点调试)。
这是标题的示例,假设rurl
上面的值
Location: http://samedomain.com/HTML/page?callback=myAsyncJSONPCallback&data=whatever_the_server_has_to_return
注意
- 的价值
data
参数可以是 JavaScript 对象字面量或 JSON 表达式,但最好是 url 编码的。
- 服务器响应的长度仅限于浏览器可以处理的 URL 长度。
另外 - 在我的系统中,服务器有一个默认值rurl
所以这个参数是可选的。但只有当客户端应用程序和服务器应用程序耦合时才能做到这一点。
发出重定向标头的 API:
http://www.webconfs.com/how-to-redirect-a-webpage.php http://www.webconfs.com/how-to-redirect-a-webpage.php
或者,您可以让服务器写入以下内容作为响应:
<script>
location.href="http://samedomain.com/HTML/page?callback=myAsyncJSONPCallback&data=whatever_the_server_has_to_return"
</script>
但 HTTP-Headers 会被认为更干净;)
与顶级文档位于同一域的实用程序页面
我使用与以下相同的实用程序页面rurl
对于我所有的发布请求:它所做的就是使用客户端代码从查询字符串中获取回调的名称和参数,并在父文档上调用它。它可以做到ONLY当此页面在与触发请求的页面完全相同的域中运行时!重要的:与 cookie 不同 - 子域不算在内!它必须具有完全相同的域。
如果此实用程序页面不包含对其他资源(包括 JS 库)的引用,也会使其更加高效。所以这个页面是纯 JavaScript。但您可以随心所欲地实施它。
这是我使用的响应者页面,其 URL 可以在rurl
POST 请求(在示例中:http://samedomain.com/utils/postResponse.html http://samedomain.com/utils/postResponse.html )
<html><head>
<script type="text/javascript">
//parse and organize all QS parameters in a more comfortable way
var params = {};
if (location.search.length > 1) {
var i, arr = location.search.substr(1).split("&");
for (i = 0; i < arr.length; i++) {
arr[i] = arr[i].split("=");
params[arr[i][0]] = unescape(arr[i][1]);
}
}
//support server answer as JavaScript Object-Literals or JSON:
// evaluate the data expression
try {
eval("params.data = " + params.data);
} catch (e) {
params.data = {error: "server response failed with evaluation error: " + e.message
,data : params.data
}
}
//invoke the callback on the parent
try{
window.parent[ params.callback ](params.data || "no-data-returned");
}catch(e){
//if something went wrong - at least let's learn about it in the
// console (in addition to the timeout)
throw "Problem in passing POST response to host page: \n\n" + e.message;
}
</script>
</head><body></body></html>
它不像 jQuery 那样自动化和“现成”库,并且涉及一些“手动”工作 - 但它有魅力:)
如果您是现成库的狂热粉丝 - 您还可以查看道场工具包当我上次检查时(大约一年前)——他们有自己的相同机制的实现。http://dojotoolkit.org/ http://dojotoolkit.org/
祝你好运,朋友,希望对你有帮助...