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

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

问题描述

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



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

解决方案

实际上 - 有一种方法可以支持POST要求。
并且在PROXI服务器中没有必要 - 只是一个小实用程序HTML页面,如下所述。



以下是如何有效地实现POST跨域电话,包括附件和多部分以及所有:)



首先是理解这个想法中的步骤,之后 - 找到一个实现样本。



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



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



什么不仅限于GET请求?



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



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



我们指定目标同一页面中IFRAME的表单。
也可以使用脚本创建,设置,命名和注入到IFR的IFRAME。



但是我们如何从用户隐藏这项工作?
我们将使用 style =display:none


在隐藏的DIV中包含FORM和IFRAME

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



但来自其他域的IFRAME无法调用它的回调顶级文件。如何克服这个问题?



事实上,如果来自FORM提交的回复是来自另一个域的页面,那么顶级页面和IFRAME中的页面导致访问被拒绝。因此服务器无法使用脚本进行回调。服务器可以做什么?的重定向即可。服务器可能会重定向到任何页面 - 包括与顶级文档位于同一域中的页面 - 可以为我们调用回调的页面。



如何才能服务器重定向?



两种方式:


  1. 使用客户端侧面脚本,如<脚本> location.href ='some-url'< / script>

  2. 使用HTTP-Header。请参阅: http://www.webconfs.com/how-to-redirect -a-webpage.php

所以我最终得到了另一个页面?它对我有何帮助?



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



所有这一页所要做的就是使用服务器的响应数据调用顶级文档的回调。客户端脚本可以访问所有URL部分,服务器可以将其响应编码为其中的一部分,以及必须调用的回调的名称。意味着 - 此页面可以是静态和HTML页面,并且不必是动态服务器端页面:)



此实用程序页面将获取来自它运行的URL - 特别是在我的实现中 - 查询字符串参数(或者您可以使用anchor-ID编写自己的实现 - 即URL的一部分直接到#符号)。由于这个页面是静态的 - 它甚至可以被缓存:)



不会为每个POST请求添加DIV,SCRIPT和IFRAME最终会泄漏记忆?



如果你将它留在页面中 - 它会。如果你在你之后清洁 - 它不会。我们所要做的就是给DIV提供一个ID,我们可以用来在服务器响应时或者超时时将DIV和FORM和IFRAME放在里面。



我们得到了什么?



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



有什么限制?




  • 服务器响应仅限于重定向的任何内容。

  • 服务器必须始终将REDIRECT返回到POST请求。这包括404和500错误。
    或者 - 在触发请求之前在客户端创建超时,这样你就有机会检测到没有返回的请求。

  • 并非所有人都能理解这一切以及所涉及的所有阶段。它是一种基础架构级别的工作,但一旦你运行它 - 它摇滚:)



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



FORM标签不是PUT和DELETE。
但是这比没有更好:)



好的,得到了​​这个概念。它是如何在技术上完成的?



我所做的是:



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



然后我撰写包含IFRAME和FORM的字符串 - 包含所有属性,属性和输入字段,并将其注入隐形DIV 。 仅在div位于DOM之后将此字符串注入DIV非常重要。如果不是 - 它将不适用于所有浏览器。



<之后 - 我获得了对FORM的引用并提交。
只需记住之前的一行 - 设置超时回调,以防服务器没有响应,或者以错误的方式响应。



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



显示代码!



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



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



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



顶级文档中的脚本代码

  //在全局范围内声明Async-call回调函数
函数myAsyncJSONPCallback(data){
//清理
var e = document.getElementById(id);
if(e)e.parentNode.removeChild(e);
clearTimeout(超时);

if(data&& data.error){
//处理错误& TIMEOUTS
// ...
返回;
}

//使用数据
// ...
}

var serverUrl =http://crossdomain.com / server / page
,params = {param1:param 1的值//我假设这个值要传递
,param2:param 2的值//这里我只是声明它...
,回调:myAsyncJSONPCallback
}
,clientUtilityUrl =http://samedomain.com/utils/postResponse.html
,id =some- unique-id//唯一请求ID。你可以用你自己的方式生成它
,div = document.createElement(DIV)//这是实际工作开始的地方!
,HTML = [< IFRAME name ='ifr _,id,'>< / IFRAME>
,< form target ='ifr _,id,'method ='POST'action =',serverUrl
,'id ='frm _,id,'enctype ='多部分/格式数据'>中
]
,每个,pval,超时;

//扩充实用程序func使数组成为StringBuffer - 请参阅下面的用法
HTML.add = function(){
for(var i = 0; i< ; arguments.length; i ++)
this [this.length] = arguments [i];
}

//将rurl添加到params对象 - 基础设施工作的一部分
params.rurl = clientUtilityUrl //实用程序页面的ABSOLUTE URL必须在
上// SAME DOMAIN作为发出请求的页面

//将所有参数添加到FORM标签中的FORM和IFRAME的组合字符串
for(每个参数){
pval = params [each] .toString()。replace(/ \/ g,& quot;); //确保:标记不会破坏
HTML.add(< input name =',each,'value =',pval,'/>); //组合字符串
}
//关闭组合字符串中的FORM标记并将所有部分放在一起
HTML.add(< / form>);
HTML = HTML.join(); //现在准备好组成的HTML字符串:)

//准备DIV
div.id = id; //此ID用于在响应到来后进行清理,或检测到超时
div.style.display =none; //确保DIV不会影响UI

// TRICKY:将DIV附加到DOM并且*仅然后*将HTML注入其中
//由于某种原因它可以在所有浏览器只有这样。注入DIV作为组合字符串的
//并不总是对我有用
document.body.appendChild(div);
div.innerHTML = HTML;

// TRICKY:注意myAsyncJSONPCallback必须看到'timeout'变量
timeout = setTimeout(myAsyncJSONPCallback({error:'TIMEOUT'}),4000);
document.getElementById(frm _+ id +)。submit();

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

 位置:http://samedomain.com/HTML/page?callback = myAsyncJSONPCallback& data = whhatever_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会被认为更干净;)



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



我使用与<$ c相同的实用程序页面$ c> rurl 用于我的所有帖子请求:它所做的只是使用客户端代码获取回调的名称和Query-String中的参数,并在父文档上调用它。当此页面在与触发请求的页面完全相同的域中运行时,它可以执行此操作! 重要提示:与Cookie不同 - 子域名不计算!!它必须与完全相同的域。



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



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

 < html>< head> 
< script type =text / javascript>
//以更舒适的方式解析和组织所有QS参数
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]);
}
}

//支持服务器答案为JavaScript Object-Literals或JSON:
//评估数据表达式
try {
eval(params.data =+ params.data);
} catch(e){
params.data = {error:服务器响应失败,评估错误:+ e.message
,data:params.data
}
}

//调用父
上的回调试试{
window.parent [params.callback](params.data ||no-data-returned );
} catch(e){
//如果出现问题 - 至少让我们在
//控制台中了解它(除了超时)
throw问题将POST响应传递给主页:\ n \ n+ e.message;
}
< / script>
< / head>< body>< / body>< / html>

这不是很多自动化和'现成'库像jQuery和involes一些'手动'工作 - 但它有魅力:)



如果你是现成的图书馆的忠实粉丝,你也可以查看 Dojo Toolkit 当我最后检查(大约一年前) - 有相同机制的自己的实现。
http://dojotoolkit.org/



祝你好运伙计,我希望它有所帮助......


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?

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.

解决方案

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's how you get Effectively a POST cross-domain call, including attached files and multi-part and all :)

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

How JSONP of jQuery is implemented, and why doesn't it support POST requests?

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.

What is not limited to GET requests?

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.

But how can we submit a FORM without refreshing the page?

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.

But How can we hide this work from the user? We'll contain both FORM and IFRAME in a hidden DIV using style="display:none"

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

But IFRAME from another domain cannot call a callback on it's top-level document. How to overcome that?

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.

How can a server redirect?

two ways:

  1. Using client side script like <Script>location.href = 'some-url'</script>
  2. Using HTTP-Header. See: http://www.webconfs.com/how-to-redirect-a-webpage.php

So I end up with another page? How does it help me?

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.

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 :)

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 :)

Won't adding for every POST request a DIV, a SCRIPT and an IFRAME eventually leak memory?

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.

What do we get?

Effectively a POST cross-domain call, including attached files and multi-part and all :)

What are the limits?

  • The server response is limited to whatever fits into a redirection.
  • The server must ALWAYS return a REDIRECT to a POST requests. That include 404 and 500 errors. Alternatively - create a timeout on the client just before firing the request, so you'll have a chance to detect requests that have not returned.
  • not everybody can understand all this and all the stages involved. it's a kind of an infrastructure level work, but once you get it running - it rocks :)

Can I use it for PUT and DELETE calls?

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

Ok, got the concept. How is it done technically?

What I do is:

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).

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).

Show me the code!

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 :)

For this example - assume the client runs on http://samedomain.com and the server runs on http://crossdomain.com.

The script code on the top-level document

//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();

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

Note that

  • 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.

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.

APIs to emit redirection header:

http://www.webconfs.com/how-to-redirect-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>

But HTTP-Headers would be considered more clean ;)

The utility page on the same domain as the top-level document

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.

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.

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: \n\n" + e.message;
}
</script>
</head><body></body></html>

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

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...

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

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