在 JSONP 和 jQuery 中使用 PUT/POST/DELETE

IT技术 javascript jquery web-services rest
2021-02-16 23:19:12

我正在创建一个支持跨域请求、JSON/JSONP 支持和主要 HTTP 方法 (PUT/GET/POST/DELETE) 的 RESTful API。现在虽然通过服务器端代码很容易访问这个 API,但最好将它暴露给 javascript。据我所知,在使用 jQuery 执行 JSONP 请求时,它仅支持 GET 方法。有没有办法使用 POST/PUT/DELETE 执行 JSONP 请求?

理想情况下,我想要一种在 jQuery 中执行此操作的方法(如果核心不支持,则通过插件),但我也将采用简单的 javascript 解决方案。任何指向工作代码或如何编码的链接都会有所帮助,谢谢。

4个回答

实际上 - 有一种方法可以支持 POST 请求。而且不需要 PROXI 服务器 - 只需一个小的实用 HTML 页面,如下所述。

以下是如何有效地获得 POST 跨域调用,包括附加文件和多部分以及所有 :)

首先是理解这个想法的步骤,然后是 - 找到一个实现示例。

jQuery的JSONP是如何实现的,为什么不支持POST请求?

虽然传统的 JSONP 是通过创建一个脚本元素并将其附加到 DOM 中实现的——这会导致浏览器触发 HTTP 请求以检索标签的源,然后将其作为 JavaScript 执行,浏览器触发的 HTTP 请求很简单 GET。

什么不限于 GET 请求?

表单。action指定跨域服务器时提交FORM FORM 标签可以完全使用脚本创建,使用脚本填充所有字段,设置所有必要的属性,注入 DOM,然后提交 - 全部使用脚本。

但是我们如何在不刷新页面的情况下提交表单呢?

我们target将表单指定为同一页面中的 IFRAME。还可以使用脚本创建、设置、命名 IFRAME 并将其注入 DOM。

但是我们如何才能对用户隐藏这项工作呢? 我们将使用 FORM 和 IFRAME 包含在隐藏的 DIV 中style="display:none"

(这是该技术最复杂的部分,请耐心等待)

但是来自另一个域的 IFRAME 无法调用其顶级文档的回调。如何克服呢?

事实上,如果来自 FORM submit 的响应是来自另一个域的页面,则顶级页面和 IFRAME 中的页面之间的任何脚本通信都会导致“访问被拒绝”。所以服务器不能使用脚本回调。服务器可以做什么?重定向服务器可以重定向到任何页面——包括与顶级文档在同一域中的页面——可以为我们调用回调的页面。

服务器如何重定向?

两种方式:

  1. 使用客户端脚本,如 <Script>location.href = 'some-url'</script>
  2. 使用 HTTP 标头。请参阅:http : //www.webconfs.com/how-to-redirect-a-webpage.php

所以我结束了另一个页面?它如何帮助我?

这是一个简单的实用程序页面,将在所有跨域调用中使用。实际上,这个页面实际上是一种代理,但它不是服务器,而是一个简单的静态 HTML 页面,任何有记事本和浏览器的人都可以使用。

此页面所要做的就是使用来自服务器的响应数据调用顶级文档上的回调。客户端脚本可以访问所有 URL 部分,服务器可以将它的响应作为它的一部分进行编码,以及必须调用的回调的名称。意思是 - 此页面可以是静态页面和 HTML 页面,而不必是动态服务器端页面 :)

这个实用程序页面将从它运行的 URL 中获取信息 - 特别是在我的实现中 - Query-String 参数(或者您可以使用锚 ID 编写自己的实现 - 即“#”右侧的 url 部分符号)。由于这个页面是静态的 - 它甚至可以被缓存:)

不会为每个 POST 请求添加一个 DIV、一个 SCRIPT 和一个 IFRAME 最终会泄漏内存吗?

如果你把它留在页面上 - 它会的。如果你在你之后清洁 - 它不会。我们所要做的就是为 DIV 提供一个 ID,只要响应从服务器到达或超时,我们就可以使用它来清理 DIV 以及其中的 FORM 和 IFRAME。

我们得到什么?

有效的 POST 跨域调用,包括附加文件和多部分以及所有 :)

有哪些限制?

  • 服务器响应仅限于适合重定向的任何内容。
  • 服务器必须始终向 POST 请求返回 REDIRECT。这包括 404 和 500 错误。或者 - 在触发请求之前在客户端上创建超时,因此您将有机会检测尚未返回的请求。
  • 不是每个人都能理解这一切以及所涉及的所有阶段。这是一种基础设施级别的工作,但是一旦你开始运行 - 它就会swing不定:)

我可以将它用于 PUT 和 DELETE 调用吗?

FORM 标签不进行 PUT 和 DELETE。但这总比没有好:)

好的,明白了这个概念。技术上是怎么做的?

我要做的是:

我创建了 DIV,将其设置为不可见,并将其附加到 DOM。我还给它一个 ID,我可以在服务器响应到达后从 DOM 中清理它(与 JQuery 清理它的 JSONP SCRIPT tasgs 的方式相同 - 但 DIV)。

然后我编写一个包含 IFRAME 和 FORM 的字符串 - 具有所有属性、属性和输入字段,并将其注入到不可见的 DIV 中。只有在 div 在 DOM 中之后,才将此字符串注入到 DIV 中是很重要的。如果没有 - 它不适用于所有浏览器。

之后 - 我获得了对表格的引用并提交。请记住之前的一行 - 设置超时回调,以防服务器不响应,或以错误的方式响应。

回调函数包含清理代码。在响应超时的情况下,它也会被计时器调用(并在服务器响应到达时清除它的超时计时器)。

给我看代码!

下面的代码片段对“纯”javascript 完全“中立”,并声明了它需要的任何实用程序。只是为了简化解释这个想法 - 它都在全局范围内运行,但是它应该更复杂一些......

尽可能在函数中组织它并参数化你需要的东西 - 但要确保所有需要互相看到的部分在同一范围内运行:)

对于此示例 - 假设客户端在http://samedomain.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,"&quot;");//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();

跨域 服务器 来自服务器的响应预计是一个 REDIRECTION,通过 HTTP-Header 或通过编写 SCRIPT 标签。(重定向更好,SCRIPT标签更容易用JS断点调试)。这是标题的示例,假设rurl值来自上面

Location: http://samedomain.com/HTML/page?callback=myAsyncJSONPCallback&data=whatever_the_server_has_to_return

注意

  • data参数的值可以是 JavaScript Object-Literal 或 JSON 表达式,但最好是 url 编码的。
  • 服务器响应的长度限于浏览器可以处理的 URL 的长度。

另外 - 在我的系统中,服务器有一个默认值,rurl因此这个参数是可选的。但是只有当您的客户端应用程序和服务器应用程序耦合时,您才能做到这一点。

用于发出重定向标头的 API:

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所有发布请求相同的实用程序页面:它所做的只是使用客户端代码从 Query-String 中获取回调的名称和参数,并在父文档上调用它。当此页面在与触发请求的页面完全相同的域中运行时,它才能执行此操作!重要提示:与 cookie 不同 - 子域不计算在内!!它必须是完全相同的域。

如果此实用程序页面不包含对其他资源(包括 JS 库)的引用,也会使其更高效。所以这个页面是纯 JavaScript 的。但是你可以随心所欲地实现它。

这是我使用的响应者页面,rurl在 POST 请求中找到了谁的 URL (在示例中: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 那样的自动化和“现成”库,并且涉及一些“手动”工作——但它有魅力:)

如果您是现成库的忠实粉丝 - 您还可以查看Dojo Toolkit,当我上次检查时(大约一年前) - 为相同的机制提供了自己的实现。 http://dojotoolkit.org/

祝你好运,希望对你有帮助...

您可以命名哪些现有的支持第 4 步的 API?“处理请求后,服务器......返回一个重定向到rurl参数”
2021-04-17 23:19:12
“4。POST 页面到服务器”——哪个服务器?我不太明白你对这种方法的描述。
2021-04-28 23:19:12
就像我Promise的那样,我添加了代码示例。我不得不从中剥离出我所从事项目的所有“秘密武器”部分,并提供最简单的方法来解释这个想法。如果您仍然认为事情需要进一步澄清,请告诉我:)
2021-04-30 23:19:12
这确实是对想象力和技术诀窍的惊人运用。太棒了!
2021-05-02 23:19:12
@Radagast:不,为第 4 步设计了哪些JSONP api
2021-05-11 23:19:12

有没有办法使用 POST/PUT/DELETE 执行 JSONP 请求?

不,没有。

那就让我给你惊喜吧。见下文:)
2021-04-20 23:19:12
@Radagast the Brown,他也询问了 DELETE 和 PUT。我在你的答案中看不到任何关于这些动词的内容,而我的答案是:-) +1 顺便说一下你的答案。这是一个很好的。
2021-04-28 23:19:12
@Darin Dimitrov,也许他正在寻找关于除了答案之外他应该尝试什么的建议。
2021-05-15 23:19:12
@Nick,他询问有关使用 jQuery 执行此操作的问题,并解释说他也将采用 javascript 解决方案,并且正在寻找工作解决方案的代码示例。我认为我的答案适用于这种情况。
2021-05-15 23:19:12

不。考虑一下什么是 JSONP:<script>在文档中注入一个新标签。浏览器执行GET请求以拉取src属性指向的脚本执行此操作时无法指定任何其他 HTTP 动词。

  • 而不是用 JSONP 方法敲我们的头,实际上默认情况下不支持 POST 方法,我们可以选择CORS。这不会对传统的编程方式带来太大的变化。通过简单的 Jquery Ajax 调用,我们可以使用跨域。
  • 在 CORS 方法中,您必须在服务器端脚本文件或服务器本身(在远程域中)中添加标头,以启用此访问。这是非常可靠的,因为我们可以防止/限制域发出不需要的呼叫。
  • 可以在维基百科页面中找到详细信息
对。当您同时控制两个域时,这就是您现在所做的。我的帖子中描述的方法在 CORS 可用之前就已经发展了。
2021-05-15 23:19:12