在 JSONP 和 jQuery 中使用 PUT/POST/DELETE [英] Using PUT/POST/DELETE with JSONP and jQuery

查看:28
本文介绍了在 JSONP 和 jQuery 中使用 PUT/POST/DELETE的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am working on creating a RESTful API that supports cross-domain requests, JSON/JSONP support, and the main HTTP method (PUT/GET/POST/DELETE). Now while will be easy to accessing this API through server side code , it would nice to exposed it to javascript. From what I can tell, when doing a JSONP requests with jQuery, it only supports the GET method. Is there a way to do a JSONP request using POST/PUT/DELETE?

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

Ideally I would like a way to do this from within jQuery (through a plugin if the core does not support this), but I will take a plain javascript solution too. Any links to working code or how to code it would be helpful, thanks.

推荐答案

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

Actually - there is a way to support POST requests. And there is no need in a PROXI server - just a small utility HTML page that is described bellow.

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

Here first are the steps in understanding the idea, after that - find an implementation sample.

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

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

While the traditional JSONP is implemented by creating a script element and appending it into the DOM - what results inforcing the browser to fire an HTTP request to retrieve the source for the tag, and then execute it as JavaScript, the HTTP request that the browser fires is simple GET.

什么不限于 GET 请求?

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

A FORM. Submit the FORM while specifing action the cross-domain server. A FORM tag can be created completely using a script, populated with all fields using script, set all necessary attributes, injected into the DOM, and then submitted - all using script.

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

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

We specify the target the form to an IFRAME in the same page. An IFRAME can also be created, set, named and injected to the DOM using script.

但是我们怎样才能对用户隐藏这项工作呢?我们将使用 style="display:none"

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

(and here's the most complicated part of the technique, be patient)

但是来自其他域的 IFRAME 无法调用其顶级文档的回调.如何克服?

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

Indeed , if a response from FORM submit is a page from another domain, any script communication between the top-level page and the page in the IFRAME results in "access denied". So the server cannot callback using a script. What can the server can do? redirect. The server may redirect to any page - including pages in the same domain as the top-level document - pages that can invoke the callback for us.

服务器如何重定向?

两种方式:

  1. 使用客户端脚本,如
  2. 使用 HTTP 标头.请参阅:http://www.webconfs.com/how-to-redirect-a-webpage.php

所以我最终会得到另一个页面?它对我有什么帮助?

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

This is a simple utility page that will be used from all cross-domain calls. Actually, this page is in-fact a kind of a proxi, but it is not a server, but a simple and static HTML page, that anybody with notepad and a browser can use.

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

All this page has to do is invoke the callback on the top-level document, with the response-data from the server. Client-Side scripting has access to all URL parts, and the server can put it's response there encoded as part of it, as well as the name of the callback that has to be invoked. Means - this page can be a static and HTML page, and does not have to be a dynamic server-side page :)

这个实用程序页面将从它运行的 URL 中获取信息——特别是在我的实现中——查询字符串参数(或者你可以使用锚 ID 编写你自己的实现——即 url 正确的部分#"标志).因为这个页面是静态的 - 它甚至可以被缓存:)

This utility page will take the information from the URL it runs in - specifically in my implementation bellow - the Query-String parameters (or you can write your own implementation using anchor-ID - i.e the part of a url right to the "#" sign). And since this page is static - it can be even allowed to be cached :)

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

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

If you leave it in the page - it will. If you clean after you - it will not. All we have to do is give an ID to the DIV that we can use to celan-up the DIV and the FORM and IFRAME inside it whenever the response arrives from the server, or times out.

我们得到了什么?

有哪些限制?

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

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

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

FORM tag does not PUT and DELETE. But that's better then nothing :)

好的,明白了.技术上是怎么做的?

我要做的是:

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

I create the DIV, style it as invisible, and append it to the DOM. I also give it an ID that I can clean it up from the DOM after the server response has arrived (the same way JQuery cleans it's JSONP SCRIPT tasgs - but the DIV).

然后我组合一个包含 IFRAME 和 FORM 的字符串 - 具有所有属性、属性和输入字段,并将其注入到不可见的 DIV 中.只有在 div 位于 DOM 中之后才将此字符串注入到 DIV 中很重要.如果没有 - 它不会在所有浏览器上工作.

Then I compose a string that contains both IFRAME and FORM - with all attributes, properties and input fields, and inject it into the invisible DIV. it is important to inject this string into the DIV only AFTER the div is in the DOM. If not - it will not work on all browsers.

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

After that - I obtain a reference to the FORM and submit it. Just remember one line before that - to set a Timeout callback in case the server does not respond, or responds in a wrong way.

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

The callback function contains the clean-up code. It is also called by timer in case of a response-timeout (and cleans it's timeout-timer when a server response arrives).

给我看代码!

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

The code snippet bellow is totally "neutral" on "pure" javascript, and declares whatever utility it needs. Just for simplification of explaining the idea - it all runs on the global scope, however it should be a little more sophisticated...

按您可能的方式组织它并参数化您需要的东西 - 但要确保所有需要相互查看的部分在同一范围内运行:)

Organize it in functions as you may and parameterize what you need - but make sure that all parts that need to see each other run on the same scope :)

对于这个例子 - 假设客户端在 http://samedomain.com 上运行,而服务器在 http://crossdomain.com.

For this example - assume the client runs on http://samedomain.com and the server runs on 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();

跨域服务器来自服务器的响应应该是重定向,通过 HTTP 标头或通过编写 SCRIPT 标记.(重定向更好,SCRIPT标签更容易用JS断点调试).这是标题的示例,假设 rurl 值来自上面

The server on the cross-domain The response from the server is expected to be a REDIRECTION, either by HTTP-Header or by writing a SCRIPT tag. (redirection is better, SCRIPT tag is easier to debug with JS breakpoints). Here's the example of the header, assuming the rurl value from above

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

注意

  • data 参数的值可以是 JavaScript Object-Literal 或 JSON 表达式,但最好采用 url 编码.
  • 服务器响应的长度限于浏览器可以处理的 URL 的长度.
  • the value of the data argument can be a JavaScript Object-Literal or JSON expression, however it better be url-encoded.
  • the length of the server response is limited to the length of a URL a browser can process.

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

Also - in my system the server has a default value for the rurl so that this parameter is optional. But you can do that only if your client-application and server-application are coupled.

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

APIs to emit redirection header:

http://www.webconfs.com/how-to-重定向-a-webpage.php

或者,您可以让服务器将以下内容作为响应写入:

Alternatively, you can have the server write as a response the following:

<script>
   location.href="http://samedomain.com/HTML/page?callback=myAsyncJSONPCallback&data=whatever_the_server_has_to_return"
</script>

但是 HTTP-Headers 会被认为更干净 ;)

But HTTP-Headers would be considered more clean ;)

与顶级文档位于同一域的实用程序页面

我使用与 rurl 相同的实用程序页面来处理我的所有发布请求:它所做的只是使用客户端代码从 Query-String 中获取回调的名称和参数,然后调用它在父文档上.当此页面在与触发请求的页面完全相同的域中运行时,它可以执行此操作!重要提示: 与 cookie 不同 - 子域不算在内!!它必须是完全相同的域.

I use the same utility page as rurl for all my post requests: all it does is take the name of the callback and the parameters from the Query-String using client side code, and call it on the parent document. It can do it ONLY when this page runs in the EXACT same domain as the page that fired the request! Important: Unlike cookies - subdomains do not count!! It has to he the exact same domain.

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

It's also make it more efficient if this utility page contains no references to other resources -including JS libraries. So this page is plain JavaScript. But you can implement it however you like.

这是我使用的响应者页面,其 URL 位于 POST 请求的 rurl 中(在示例中:http://samedomain.com/utils/postResponse.html )

Here's the responder page that I use, who's URL is found in the rurl of the POST request (in the example: 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: 

" + e.message;
}
</script>
</head><body></body></html>

它不是像 jQuery 那样的自动化和现成"库,并且涉及一些手动"工作 - 但它有魅力:)

It's not much automation and 'ready-made' library like jQuery and involes some 'manual' work - but it has the charm :)

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

If you're a keen fan of ready-made libraries - you can also check on Dojo Toolkit that when last I checked (about a year ago) - had their own implementation for the same mechanism. http://dojotoolkit.org/

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

Good luck buddy, I hope it helps...

这篇关于在 JSONP 和 jQuery 中使用 PUT/POST/DELETE的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆