Javascript 在 JSONP 和 jQuery 中使用 PUT/POST/DELETE
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5345493/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using PUT/POST/DELETE with JSONP and jQuery
提问by ryanzec
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?
我正在创建一个支持跨域请求、JSON/JSONP 支持和主要 HTTP 方法 (PUT/GET/POST/DELETE) 的 RESTful API。现在虽然通过服务器端代码很容易访问这个 API,但最好将它暴露给 javascript。据我所知,在使用 jQuery 执行 JSONP 请求时,它仅支持 GET 方法。有没有办法使用 POST/PUT/DELETE 执行 JSONP 请求?
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.
理想情况下,我想要一种在 jQuery 中执行此操作的方法(如果核心不支持,则通过插件),但我也将采用简单的 javascript 解决方案。任何指向工作代码或如何编码的链接都会有所帮助,谢谢。
回答by Radagast the Brown
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.
实际上 - 有一种方法可以支持 POST 请求。并且不需要 PROXI 服务器 - 只需一个小的实用 HTML 页面,如下所述。
Here's how you get Effectively a POST cross-domain call, including attached files and multi-part and all :)
这是您如何有效地获得 POST 跨域调用,包括附加文件和多部分以及所有 :)
Here first are the steps in understandingthe idea, after that - find an implementation sample.
首先是理解这个想法的步骤,然后是 - 找到一个实现示例。
How JSONP of jQuery is implemented, and why doesn't it support POST requests?
jQuery的JSONP是如何实现的,为什么不支持POST请求?
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.
虽然传统的 JSONP 是通过创建一个脚本元素并将其附加到 DOM 中实现的——这会导致浏览器触发 HTTP 请求以检索标签的源,然后将其作为 JavaScript 执行,浏览器触发的 HTTP 请求很简单 GET。
What is not limited to GET requests?
什么不限于 GET 请求?
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.
表单。action
指定跨域服务器时提交FORM 。FORM 标签可以完全使用脚本创建,使用脚本填充所有字段,设置所有必要的属性,注入 DOM,然后提交 - 全部使用脚本。
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.
我们target
将表单指定为同一页面中的 IFRAME。还可以使用脚本创建、设置、命名 IFRAME 并将其注入 DOM。
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"
但是我们如何才能对用户隐藏这项工作呢?我们将使用 FORM 和 IFRAME 包含在隐藏的 DIV 中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?
但是来自另一个域的 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.
事实上,如果来自 FORM submit 的响应是来自另一个域的页面,则顶级页面和 IFRAME 中的页面之间的任何脚本通信都会导致“访问被拒绝”。因此服务器无法使用脚本进行回调。服务器可以做什么?重定向。服务器可以重定向到任何页面 - 包括与顶级文档在同一域中的页面 - 可以为我们调用回调的页面。
How can a server redirect?
服务器如何重定向?
two ways:
两种方式:
- Using client side script like
<Script>location.href = 'some-url'</script>
- Using HTTP-Header. See: http://www.webconfs.com/how-to-redirect-a-webpage.php
- 使用客户端脚本,如
<Script>location.href = 'some-url'</script>
- 使用 HTTP 标头。请参阅: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.
这是一个简单的实用程序页面,将用于所有跨域调用。实际上,这个页面实际上是一种代理,但它不是服务器,而是一个简单的静态 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 部分,服务器可以将它的响应作为它的一部分进行编码,以及必须调用的回调的名称。意思是 - 此页面可以是静态页面和 HTML 页面,而不必是动态服务器端页面 :)
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 :)
这个实用程序页面将从它运行的 URL 中获取信息 - 特别是在我的实现中 - 查询字符串参数(或者您可以使用锚 ID 编写自己的实现 - 即“#”右侧的 url 部分标志)。由于这个页面是静态的 - 它甚至可以被缓存:)
Won't adding for every POST request a DIV, a SCRIPT and an IFRAME eventually leak memory?
不会为每个 POST 请求添加一个 DIV、一个 SCRIPT 和一个 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.
如果你把它留在页面上 - 它会的。如果你在你之后清洁 - 它不会。我们所要做的就是为 DIV 提供一个 ID,只要响应从服务器到达或超时,我们就可以使用它来清理 DIV 以及其中的 FORM 和 IFRAME。
What do we get?
我们得到什么?
Effectively a POST cross-domain call, including attached files and multi-part and all :)
有效的 POST 跨域调用,包括附加文件和多部分以及所有 :)
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 :)
- 服务器响应仅限于适合重定向的任何内容。
- 服务器必须始终向 POST 请求返回 REDIRECT。这包括 404 和 500 错误。或者 - 在触发请求之前在客户端上创建超时,因此您将有机会检测尚未返回的请求。
- 不是每个人都能理解这一切以及所涉及的所有阶段。这是一种基础设施级别的工作,但是一旦你开始运行 - 它就会摇摆不定:)
Can I use it for PUT and DELETE calls?
我可以将它用于 PUT 和 DELETE 调用吗?
FORM tag does not PUT and DELETE. But that's better then nothing :)
FORM 标签不进行 PUT 和 DELETE。但这总比没有好:)
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).
我创建了 DIV,将其设置为不可见,并将其附加到 DOM。我还给它一个 ID,我可以在服务器响应到达后从 DOM 中清理它(与 JQuery 清理它的 JSONP SCRIPT tasgs 的方式相同 - 但 DIV)。
Then I composea 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.
然后我编写一个包含 IFRAME 和 FORM 的字符串 - 具有所有属性、属性和输入字段,并将其注入到不可见的 DIV 中。只有在 div 位于 DOM 中之后,才能将此字符串注入到 DIV 中,这一点很重要。如果没有 - 它不适用于所有浏览器。
After that - I obtain a reference to the FORM and submit it. Just remember one line before that - to set a Timeout callbackin 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...
下面的代码片段对“纯”javascript 完全“中立”,并声明了它需要的任何实用程序。只是为了简化解释这个想法 - 它都在全局范围内运行,但是它应该更复杂一些......
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.comand the server runs on http://crossdomain.com.
对于此示例 - 假设客户端在http://samedomain.com上运行,服务器在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,""");//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-domainThe 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
跨域服务器 来自服务器的响应预计是一个 REDIRECTION,通过 HTTP-Header 或通过编写 SCRIPT 标签。(重定向更好,SCRIPT标签更容易用JS断点调试)。这是标题的示例,假设rurl
值来自上面
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.
data
参数的值可以是 JavaScript Object-Literal 或 JSON 表达式,但最好是 url 编码的。- 服务器响应的长度限于浏览器可以处理的 URL 的长度。
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.
另外 - 在我的系统中,服务器有一个默认值,rurl
因此这个参数是可选的。但是只有当您的客户端应用程序和服务器应用程序耦合时,您才能做到这一点。
APIs to emit redirection header:
用于发出重定向标头的 API:
http://www.webconfs.com/how-to-redirect-a-webpage.php
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 ;)
但是 HTTP-Headers 会被认为更干净;)
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 ONLYwhen 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.
我使用与rurl
所有发布请求相同的实用程序页面:它所做的只是使用客户端代码从 Query-String 获取回调的名称和参数,并在父文档上调用它。只有当此页面在与触发请求的页面完全相同的域中运行时,它才能执行此操作!重要提示:与 cookie 不同 - 子域不计算在内!!它必须是完全相同的域。
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.
如果此实用程序页面不包含对其他资源(包括 JS 库)的引用,这也会提高效率。所以这个页面是纯 JavaScript 的。但是你可以随心所欲地实现它。
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)
这是我使用的响应者页面,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>
It's not much automation and 'ready-made' library like jQuery and involes some 'manual' work - but it has the charm :)
它不是像 jQuery 那样的自动化和“现成”库,并且涉及一些“手动”工作——但它有魅力:)
If you're a keen fan of ready-made libraries - you can also check on Dojo Toolkitthat when last I checked (about a year ago) - had their own implementation for the same mechanism. http://dojotoolkit.org/
如果您是现成库的忠实粉丝——您还可以查看Dojo Toolkit,当我上次检查时(大约一年前)——有他们自己的相同机制的实现。 http://dojotoolkit.org/
Good luck buddy, I hope it helps...
祝你好运,希望对你有帮助...
回答by Darin Dimitrov
Is there a way to do a JSONP request using POST/PUT/DELETE?
有没有办法使用 POST/PUT/DELETE 执行 JSONP 请求?
No there isn't.
不,没有。
回答by Wayne
No. Consider what JSONP is: an injection of a new <script>
tag in the document. The browser performs a GET
request to pull the script pointed to by the src
attribute. There's no way to specify any other HTTP verb when doing this.
不。考虑一下什么是 JSONP:<script>
在文档中注入一个新标签。浏览器执行GET
请求以拉取src
属性指向的脚本。执行此操作时无法指定任何其他 HTTP 动词。
回答by Ganesh Babu
- Rather than banging our heads with JSONP method, that actually won't support POST method by default, we can go for CORS.That will provide no big changes in the conventional way of programming. By simple Jquery Ajax call we can go with cross domains.
- In CORS method, you have to add headers in server side scripting file, or in the server itself(in remote domain), for enabling this access. This is much reliable, since we can prevent/restrict the domains making unwanted calls.
- It can be found in detail in wikipediapage.